数据结构·一篇搞定顺序表!

大家好啊,几日不见,甚是想念,从这一篇文章开始,我们就要进入数据结构了哦,那么我们废话不多说,今天我们一起来搞定顺序表!!!
请添加图片描述

1. 顺序表概念及结构

顺序表是一种线性结构,它由一组连续的存储单元(通常是数组)组成,数据元素之间的关系是相邻的。顺序表中的元素是按照一定的顺序排列的,可以通过下标来访问和操作各个元素。顺序表的结构简单、易于实现,是一种常见的数据结构之一。

2. 顺序表分类

• 顺序表和数组的区别
◦ 顺序表的底层结构是数组,对数组的封装,实现了常⽤的增删改查等接口

静态顺序表

#define MAX_SIZE 10

typedef struct {
    int data[MAX_SIZE];
    int length;
} StaticList;

在这个例子中,我们定义了一个包含10个整数元素的静态顺序表,其中data数组存储元素,length表示当前顺序表中元素的个数。在使用静态顺序表时,需要注意不要超出预先定义的最大长度,否则可能导致数组越界错误.

动态顺序表

在这里插入图片描述
我们一般使用顺序表都是动态顺序表,这样的话才可以进行增删改查的操作,那么我们接下来就来康康这些操作~

3. 实现动态顺序表

#define INIT_CAPACITY 4
typedef int SLDataType;
// 动态顺序表 -- 按需申请
typedef struct SeqList
{
 SLDataType* a;
 int size; // 有效数据个数
 int capacity; // 空间容量
}SL;
//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPrint(SL* ps);
//扩容
void SLCheckCapacity(SL* ps);
//头部插⼊删除 / 尾部插⼊删除
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
//指定位置之前插⼊/删除数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
int SLFind(SL* ps, SLDataType x);

下面是实现的代码

#define INIT_CAPACITY 4  
  
typedef int SLDataType;  
  
typedef struct SeqList {  
    SLDataType* a;  
    int size; // 有效数据个数  
    int capacity; // 空间容量  
} SL;  

3.1 初始化

// 初始化  
void SLInit(SL* ps) {  
    ps->a = (SLDataType*)malloc(INIT_CAPACITY * sizeof(SLDataType));  
    if (!ps->a) {  
        exit(EXIT_FAILURE); // 分配内存失败  
    }  
    ps->size = 0;  
    ps->capacity = INIT_CAPACITY;  
}  
  1. 通过 malloc 函数为顺序线性表的数组成员 a 分配内存空间,大小为 INIT_CAPACITY *
    sizeof(SLDataType),其中 INIT_CAPACITY 是一个预先定义的常量,表示初始容量。
  2. 检查内存分配是否成功,如果分配失败(即 ps->a 为 NULL),则调用 exit(EXIT_FAILURE)
    函数退出程序。
  3. 将顺序线性表的 size 成员初始化为 0,表示当前表中没有元素。
  4. 将顺序线性表的 capacity 成员初始化为INIT_CAPACITY,表示表的初始容量为 INIT_CAPACITY。

3.2 销毁

void SLDestroy(SL* ps) {  
    free(ps->a);  
    ps->a = NULL;  
    ps->size = 0;  
    ps->capacity = 0;  
}  
  1. 调用 free 函数释放顺序线性表的数组成员 a 所占用的内存空间,这样可以避免内存泄漏。
  2. 将顺序线性表的 a 成员指针设置为 NULL,这是一种良好的习惯,可以避免出现野指针访问的问题。
  3. 将顺序线性表的 size 成员设为 0,表示表中不再有元素。
  4. 将顺序线性表的 capacity 成员设为 0,表示表的容量为 0。

3.3 打印

void SLPrint(SL* ps) {  
    for (int i = 0; i < ps->size; i++) {  
        printf("%d ", ps->a[i]);  
    }  
    printf("\n");  
}  

这个没什么好说的吧哈哈哈

3.4 扩容

void SLCheckCapacity(SL* ps) {  
    if (ps->size == ps->capacity) {  
        ps->capacity *= 2;  
        SLDataType* temp = (SLDataType*)realloc(ps->a, ps->capacity * sizeof(SLDataType));  
        if (!temp) {  
            exit(EXIT_FAILURE); // 扩容失败  
        }  
        ps->a = temp;  
    }  
}  
  1. 判断顺序线性表的当前大小(size)是否等于容量(capacity),如果相等,则表示表中元素已经填满,需要进行扩容操作。
  2. 将顺序线性表的容量 capacity 扩大为原来的两倍,即 ps->capacity *= 2;,这里采用了简单的扩容策略。
  3. 调用 realloc 函数重新分配内存空间,将原来的数据拷贝到新的空间中,大小为 ps->capacity *
    sizeof(SLDataType)。
  4. 检查是否成功分配新的内存空间,如果分配失败(即 temp 为 NULL),则调用 exit(EXIT_FAILURE)
    函数退出程序。
  5. 将新分配的内存空间的地址赋值给顺序线性表的数组成员 a,完成扩容操作。

3.5 头部插入

void SLPushFront(SL* ps, SLDataType x) {  
    SLCheckCapacity(ps);  
    for (int i = ps->size; i > 0; i--) {  
        ps->a[i] = ps->a[i - 1];  
    }  
    ps->a[0] = x;  
    ps->size++;  
}  
  1. 调用 SLCheckCapacity 函数,确保顺序线性表有足够的容量来存储新的元素。
  2. 从顺序线性表的最后一个元素开始,依次将元素向后移动一个位置,为新元素腾出空间。这里使用循环从 ps->size
    开始,依次向前移动元素,直到第一个元素。
  3. 将要插入的新元素 x 放入顺序线性表的第一个位置,即头部位置。
  4. 增加顺序线性表的大小 size,表示表中元素数量增加了一个。

3.6 头部删除

void SLPopFront(SL* ps) {  
    if (ps->size == 0) {  
        return;  
    }  
    for (int i = 0; i < ps->size - 1; i++) {  
        ps->a[i] = ps->a[i + 1];  
    }  
    ps->size--;  
}  
  1. 判断顺序线性表的大小(size)是否为0,如果是空表(size == 0),则直接返回,不进行删除操作。
  2. 从顺序线性表的第一个元素开始,依次将元素向前移动一个位置,覆盖掉原来的元素。这里使用循环从0开始,依次向后移动元素,直到倒数第二个元素。
  3. 减少顺序线性表的大小 size,表示表中元素数量减少了一个。

3.7 尾部插入

void SLPushBack(SL* ps, SLDataType x) {  
    SLCheckCapacity(ps);  
    ps->a[ps->size] = x;  
    ps->size++;  
}  
  1. 调用 SLCheckCapacity 函数,确保顺序线性表有足够的容量来存储新的元素。
  2. 将要插入的新元素 x 放入顺序线性表当前大小(size)的位置,即在尾部插入。
  3. 增加顺序线性表的大小 size,表示表中元素数量增加了一个。

3.8 尾部删除

不用说吧~~~

void SLPopBack(SL* ps) {  
    if (ps->size == 0) {  
        return;  
    }  
    ps->size--;  
}  

3.9 指定位置之前插入数据

void SLInsert(SL* ps, int pos, SLDataType x) {  
    if (pos < 0 || pos > ps->size) {  
        return; // 插入位置不合理  
    }  
    SLCheckCapacity(ps);  
    for (int i = ps->size; i > pos; i--) {  
        ps->a[i] = ps->a[i - 1];  
    }  
    ps->a[pos] = x;  
    ps->size++;  
}  
  1. 判断插入位置 pos 是否合理,即必须满足 pos >= 0 且 pos <= ps->size,否则直接返回,不进行插入操作。
  2. 调用 SLCheckCapacity 函数,确保顺序线性表有足够的容量来存储新的元素。
  3. 从顺序线性表的最后一个元素开始,依次将元素向后移动一个位置,为新元素腾出空间。这里使用循环从 ps->size开始,依次向前移动元素,直到插入位置 pos。
  4. 将要插入的新元素 x 放入顺序线性表的指定位置 pos。
  5. 增加顺序线性表的大小 size,表示表中元素数量增加了一个.

3.10 指定位置删除数据

void SLErase(SL* ps, int pos) {  
    if (pos < 0 || pos >= ps->size) {  
        return; // 删除位置不合理  
    }  
    for (int i = pos; i < ps->size - 1; i++) {  
        ps->a[i] = ps->a[i + 1];  
    }  
    ps->size--;  
}  
  1. 判断删除位置 pos 是否合理,即必须满足 pos >= 0 且 pos < ps->size,否则直接返回,不进行删除操作。
  2. 从指定位置 pos 开始,将后面的元素依次向前移动一个位置,覆盖掉要删除的元素。这里使用循环从 pos
    开始,依次将元素向前移动,直到倒数第二个元素。
  3. 减少顺序线性表的大小 size,表示表中元素数量减少了一个,相当于删除了一个元素。

3.11 查找数据

int SLFind(SL* ps, SLDataType x) {  
    for (int i = 0; i < ps->size; i++) {  
        if (ps->a[i] == x) {  
            return i; // 返回数据下标  
        }  
    }  
    return -1; // 没有找到数据  
}  

4. 基于动态顺序表实现通讯录项目

相信大家掌握了上面的操作后,实现通讯录并不是啥难事,肖恩在这也不细细讲解了,希望大家理解哦~~~请添加图片描述

由于这个通讯录的代码有一点点多,放在这里大家也不好学习,我直接放个链接吧,大家直接点开就欧克了,也方便copy.
通讯录

5. 顺序表经典算法

这是两个有关顺序表的经典的算法的,大家点开链接可以做做,同样,我把代码放在这,大家自行悟道
经典算法OJ题1: 移除元素

int removeElement(int* nums, int numsSize, int val) {
        int i, j;
    for(i = 0, j = 0; i < numsSize; i++){
        if(nums[i] != val){
            nums[j] = nums[i];
            j++;
        }
    }
    return j;
}

经典算法OJ题2: 合并两个有序数组

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
int p1 = m - 1;
    int p2 = n - 1;
    int p = m + n - 1;
    
    while(p1 >= 0 && p2 >= 0){
        if(nums1[p1] > nums2[p2]){
            nums1[p] = nums1[p1];
            p1--;
        } else {
            nums1[p] = nums2[p2];
            p2--;
        }
        p--;
    }
    
    while(p2 >= 0){
        nums1[p] = nums2[p2];
        p2--;
        p--;
    }
}

大家要是有比这个好的方法那就更好了!还是比肖恩强的哈哈哈

6. 顺序表的问题及思考*

  1. 中间/头部的插入删除,时间复杂度为O(N)
  2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

思考:如何解决以上问题呢?

在这里插入图片描述
下期预告
那当然是…Linked List

  • 24
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
顺序表是一种基于数组的数据结构,可以存储一组连续的元素。下面是构造一个顺序表的示例代码: ```c #define MAXSIZE 100 // 定义顺序表最大长度 typedef struct { int data[MAXSIZE]; // 存储数据的数组 int length; // 当前长度 } SqList; // 初始化顺序表 void InitList(SqList *L) { L->length = 0; } // 插入元素 int InsertList(SqList *L, int pos, int elem) { if (pos < 1 || pos > L->length + 1 || L->length >= MAXSIZE) { return 0; // 插入位置不合法或者顺序表已满,插入失败 } for (int i = L->length; i >= pos; i--) { L->data[i] = L->data[i - 1]; // 将pos及之后的元素后移 } L->data[pos - 1] = elem; // 插入新元素 L->length++; // 长度加1 return 1; // 插入成功 } // 删除元素 int DeleteList(SqList *L, int pos) { if (pos < 1 || pos > L->length) { return 0; // 删除位置不合法,删除失败 } for (int i = pos; i < L->length; i++) { L->data[i - 1] = L->data[i]; // 将pos及之后的元素前移 } L->length--; // 长度减1 return 1; // 删除成功 } // 查找元素 int FindList(SqList *L, int elem) { for (int i = 0; i < L->length; i++) { if (L->data[i] == elem) { return i + 1; // 返回元素在顺序表中的位置 } } return 0; // 未找到元素 } // 输出顺序表 void PrintList(SqList *L) { for (int i = 0; i < L->length; i++) { printf("%d ", L->data[i]); } printf("\n"); } ``` 这里定义了一个 `SqList` 结构体,其中 `data` 是存储数据的数组,`length` 是当前长度。`InitList` 函数用于初始化顺序表,将长度设为0。`InsertList` 函数用于在指定位置插入元素,如果位置不合法或者顺序表已满,返回0表示插入失败;否则将插入位置及之后的元素后移,插入新元素,长度加1,返回1表示插入成功。`DeleteList` 函数用于删除指定位置的元素,如果位置不合法,返回0表示删除失败;否则将删除位置及之后的元素前移,长度减1,返回1表示删除成功。`FindList` 函数用于查找指定元素在顺序表中的位置,如果未找到,返回0表示未找到。`PrintList` 函数用于输出顺序表中的所有元素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值