一、顺序表的特点
二、顺序表实现
- 1.数据结构
- 2.数据结构 *** 作实现
- 3.动态扩容
一、顺序表的特点
- 随机访问,通过首地址和元素序号就能在时间**O(1)**内找到元素
- 存储密度高,每个结点只能存数据元素 (链表还要在结点存下一个元素地址)
- 逻辑上相邻的元素在 物理上也相邻
- 线性表的元素位序(序号)是从1开始的,数组的序号才是从0开始的
二、顺序表实现 1.数据结构
动态分配实现 代码如下(示例):
#define MAXSIZE 50
typedef struct Vector{
// 数据元素
int* data;
// 顺序表的最大数据量和当前元素数
int size,length;
};
静态分配实现 代码如下(示例):
#define MAXSIZE 50
typedef struct Vector{
// 数据元素
int data[MAXSIZE];
// 顺序表当前的长度
int length;
};
动静态实现在代码上的区别就是一个使用的数组存储数据, 而动态分配使用的是指针动态向系统申请存储空间
动态分配和静态分配 : 动态分配不同于链式存储, 它在物理结构还还是顺序存储,在内存中元素还是占用连续的存储空间,仍是随机存取 , 只是 为它分配的存储空间是在运行时动态分配的(可扩容)
下面的数据结构的 *** 作使用的都是动态分配
2.数据结构 *** 作实现插入 代码如下(示例):
/**
* 顺序表元素插入
* @param vec,ind,val 顺序表,插入的位置,插入的元素
* @return 0-插入失败 1-插入成功
* */
int insert(Vector* vec, int ind, int val){
// 合法性判断
// 顺序表判空
if(vec == NULL) return 0;
//顺序表已经满了的时候,不能再插入了
if(vec->length == vec->size) {
if(!extend(vec)){ //动态扩容
return 0;
}
printf("顺序表大小以扩容至%d \n",vec->size);
}
// 插入位置的合法性判断
if(ind < 0 || ind > vec->length) return 0;
// 插入位置后一位开始, 集体向后移动一位
for (int i = vec->length; i > ind; i--) {
vec->data[i] = vec->data[i - 1];
}
// 插入
vec->data[ind] = val;
// 修改当前元素数量
vec->length += 1;
return 1;
}
插入元素的平均移动次数 : ( 1 / ( n + 1 ) ) ∑ i = 1 n + 1 ( n − i + 1 ) = n / 2 (1/(n+1))\sum_{i=1}^{n+1}(n-i+1) =n/2 (1/(n+1))∑i=1n+1(n−i+1)=n/2
平均时间复杂度 : O ( n ) O(n) O(n)
删除 代码如下(示例):
/**
* 顺序表元素删除
* @param vec,ind 顺序表,删除的位置
* @return 0-删除失败 1-删除成功
* */
int erase(Vector* vec, int ind){
// 合法性判断
// 顺序表为空
if(vec == NULL) return 0;
// 顺序表中没有元素 (顺序表为空表示这段)
if(vec->length == 0)return 0;
if(ind < 0 || ind >=vec->length) return 0;
// 开始删除元素
for (int i = ind + 1; i < vec->length; ++i) {
vec->data[i - 1] = vec->data[i];
}
// 修改当前元素数量
vec->length -= 1;
return 1;
}
删除元素的平均移动次数 : ( 1 / n ) ∑ i = 1 n ( n − i ) = ( n − 1 ) / 2 (1/n)\sum_{i=1}^{n}(n-i) =(n-1)/2 (1/n)∑i=1n(n−i)=(n−1)/2
平均时间复杂度 : O ( n ) O(n) O(n)
查找 代码如下(示例):
/**
* 顺序表查找第一个元素值为val的索引
* @param vec,val
* @return 元素的索引
* */
int find(Vector* vec,int val){
// 判空处理
if(vec == NULL) {
printf("传入顺序表为空");
return 0;
}
// 循环查找
for (int i = 0; i < vec->length; ++i) {
if(vec->data[i] == val){
return i+1; // 返回索引(规定线性表的索引从1开始)
}
}
printf("未找到目标值元素");
return 0;
}
查找元素的平均比较次数 : ( 1 / n ) ∑ i = 1 n i = ( n + 1 ) / 2 (1/n)\sum_{i=1}^{n}i=(n+1)/2 (1/n)∑i=1ni=(n+1)/2
平均时间复杂度 : O ( n ) O(n) O(n)
3.动态扩容
/**
* 顺序表扩容 *** 作
* @param vec 顺序表
* @return 0-扩容失败 1-扩容成功
* */
int extend(Vector* vec){
int new_size = vec->size;
int* p = (int*)realloc(vec->data,sizeof(int) * new_size);
if(p == NULL) return 0; //扩容失败
vec->size = new_size;
vec->data = p;
return 1;
}
为什么动态扩容不能直接使用这段代码
vec->size *= 2;
vec->data = (int*) realloc(vec->data,sizeof(int) * vec->size);
realloc()
的功能是给一个已经分配了地址的指针重新分配空间,参数1为原有的空间地址,参数2是重新申请的地址长度
当扩大一块内存空间时,realloc()
试图直接从堆上现存的数据后面的那些字节中获得附加的字节, 如果无法获取成功,则可能会导致内存泄漏, 因此需要一个临时指针变量去接收函数返回的值, 避免 vec->data
的地址永久丢失
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)