【数据结构与算法 C/C++】顺序表实现与动态分配空间


一、顺序表的特点

  1. 随机访问,通过首地址和元素序号就能在时间**O(1)**内找到元素
  2. 存储密度高,每个结点只能存数据元素 (链表还要在结点存下一个元素地址)
  3. 逻辑上相邻的元素在 物理上也相邻
  4. 线性表的元素位序(序号)是从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(ni+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(ni)=(n1)/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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值