顺序表的定义和特点
顺序表的定义
在讲顺序表之前,我们先大致了解一下线性表。线性表在逻辑上是一种线性的数据结构。可以把线性表看成一条连续的直线。但是在物理结构上不一定是连续的,线性表在存储时,通常是以数组或者链式结构的形式存储的。
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串等…
顺序表的概念:顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表的特点
线性表用于存储逻辑关系为“一对一”的数据,顺序表自然也不例外。
不仅如此,顺序表对数据的物理存储结构也有要求。 顺序表存储数据时,会提前申请一整块足够大小的物理空间,然后将数据依次存储起来,存储时做到数据元素之间不留一丝缝隙。
例如,使用顺序表存储集合{1,2,3,4,5}数据最终的存储状态如下图所示:
顺序表的基本运算
顺序表是一种线性数据结构,可以进行多种运算操作。以下是基本的顺序表运算:
- 插入操作:在顺序表的指定位置插入一个元素。这可以通过将插入位置后的元素依次向后移动来实现。
- 删除操作:从顺序表中删除指定位置的元素。这可以通过将删除位置后的元素依次向前移动来实现。
- 查找操作:在顺序表中查找指定元素的位置。可以使用线性搜索或二分搜索等算法来实现。
- 更新操作:修改顺序表中指定位置的元素的值。
- 求长度操作:计算顺序表中元素的个数。
顺序表的实现
定义顺序表结构体
// 顺序表的最大长度
#define MAX_SIZE 100
// 定义顺序表结构体
typedef struct {
int data[MAX_SIZE]; // 存储数据元素
int length; // 长度
} SeqList;
// 初始化顺序表
void InitList(SeqList *L) {
L->length = 0;
}
顺序表末尾添加数据
顺序表结构至此已经搭建好了,下一步便是往表上填数据。往顺序表末尾添加数据首先当然要判断一下表是否已满。如果表没有满,就往表中添加数据并且表的长度+1
// 判断顺序表是否已满
int IsFull(SeqList L) {
return L.length == MAX_SIZE;
}
// 判断顺序表是否为空
int IsEmpty(SeqList L) {
return L.length == 0;
}
// 在顺序表末尾插入元素
void InsertList(SeqList *L, int x) {
if (IsFull(*L)) {
printf("Error: List is full\n");
return;
}
L->data[L->length++] = x;
}
顺序表操作
添加完数据后,就是便是对自己所创建的顺序表的修改。顺序表操作包括:增,删,改,查。
插入元素
向已有顺序表中插入数据元素,根据插入位置的不同,可分为以下 3 种情况:
- 插入到顺序表的表头
- 在表的中间位置插入元素
- 在表的末尾位置插入元素(刚才代码已经实现)
虽然数据元素插入顺序表中的位置有所不同,但是都使用的是同一种方式去解决,即:通过遍历,找到数据元素要插入的位置。
// 在顺序表的第pos个元素之前插入元素x
void InsertListPos(SeqList *L, int pos, int x) {
if (L->length >= MAX_SIZE) {
printf("Error: List is full\n");
return;
}
if (pos < 0 || pos > L->length) {
printf("Error: Invalid position\n");
return;
}
for (int i = L->length; i > pos; i--) {
L->data[i] = L->data[i-1];
}
L->data[pos] = x;
L->length++;
}
在插入之前,需要判断顺序表是否已满以及要插入的位置是否合法。然后,将后面所有元素向后移动一位,并将要插入的元素插入到指定位置。最后,更新顺序表的长度。
删除元素
从顺序表中删除指定元素,实现起来非常简单,只需找到目标元素,并将其后续所有元素整体前移 1 个位置即可。
// 从顺序表中删除指定元素x
int DeleteListElement(SeqList *L, int x) {
int i;
for (i = 0; i < L->length; i++) {
if (L->data[i] == x) {
break;
}
}
if (i == L->length) {
printf("Error: Element not found\n");
return -1;
}
for (int j = i; j < L->length - 1; j++) {
L->data[j] = L->data[j+1];
}
L->length--;
return 0;
}
其中,参数SeqList *L表示顺序表的指针,int x表示要删除的元素。在删除之前,需要遍历顺序表,找到要删除的元素的位置。如果找到了,就将后面所有元素向前移动一位,并将顺序表的长度减1。如果没找到,就输出错误信息。最后返回0表示删除成功。
更新元素
顺序表更改元素的实现过程是:
- 找到目标元素
- 直接修改该元素的值
void UpdateListElement(SeqList *L, int pos, int new_val) {
if (pos < 0 || pos >= L->length) {
printf("Error: Invalid position\n");
return;
}
L->data[pos] = new_val;
}
其中,参数SeqList *L表示顺序表的指针,int pos表示要更新的位置,int new_val表示要更新的值。在更新之前,需要判断要更新的位置是否合法。然后,将顺序表中该位置上的元素更新为新值。
查找元素
// 在顺序表中查找指定元素x的位置
int SearchListElement(SeqList L, int x) {
for (int i = 0; i < L.length; i++) {
if (L.data[i] == x) {
return i;
}
}
return -1;
}
其中,参数SeqList L表示顺序表,int x表示要查找的元素。在查找之前,遍历顺序表中的所有元素,如果找到了元素x,就返回它的位置。如果没找到,就返回-1表示查找失败。
打印顺序表中的元素
最后写一个方法实现打印顺序表中的元素
// 打印顺序表中的元素
void PrintList(SeqList L) {
int i;
for (i = 0; i < L.length; i++) {
printf("%d ", L.data[i]);
}
printf("\n");
}
运行截图
上面写了那么多的代码,我们总该调用他们试试看,查看自己的代码有没有什么问题和运行的结果
这个是程序演示的列表选项
插入数据,先往顺序表末尾插入一个元素1,接着往顺序表首位插入一个元素2
删除掉了元素1,现在顺序表里面只剩一个元素2
将顺序表里面的元素2更新成元素4
查询元素所在的位置,(因为之前顺序表里面只有一个元素,所以又往里面添加多个元素),我们现在查询到元素5所在的位置为4(注意从0开始的)
因为顺序表里面有元素,现在顺序表不为空
我们之前定义的顺序表最大长度100,现在表里才有4个元素,所以顺序表肯定没满呢
打印出顺序表
按0退出程序
完整代码
最后附上 完整代码 , c语言顺序表 存储结构。
小结
顺序表是一种线性表数据结构,使用一段连续的存储空间来存储数据元素。其主要特点是:
- 顺序表中的所有元素都存储在一块连续的存储空间中,相邻元素之间的存储位置是连续的。
- 顺序表中的元素可以按索引随机访问,访问速度快。
- 顺序表在插入和删除元素时,需要移动大量元素,效率较低。
顺序表的基本运算包括:
- 初始化顺序表:为顺序表分配存储空间,并将元素初始化为默认值。
- 插入元素:在顺序表的指定位置插入一个新元素。
- 删除元素:删除顺序表中指定位置的元素。
- 修改元素:修改顺序表中指定位置的元素值。
- 查询元素:查询顺序表中指定位置的元素值。
- 遍历顺序表:依次访问顺序表中的每个元素。
在C语言中,可以使用数组来实现顺序表。具体实现方法如下:
- 定义结构体类型SeqList,包含数组data和长度length两个成员变量。
- 初始化顺序表时,为数组data分配存储空间,并将元素初始化为默认值。
- 插入元素时,先将插入位置及其之后的元素向后移动一位,再将新元素插入到指定位置。
- 删除元素时,先将删除位置及其之后的元素向前移动一位,再释放被删除元素的存储空间。
- 修改元素时,直接修改指定位置的元素值即可。
- 查询元素时,直接返回指定位置的元素值即可。
- 遍历顺序表时,使用for循环依次访问数组data中的每个元素。
总体来说,顺序表是一种简单直观的数据结构,适用于需要随机访问元素的场景。但由于其在插入和删除元素时需要移动大量元素,效率较低,因此在需要频繁插入和删除元素的场景中不太适用。
参考文献
[1] 李刚 刘万辉. "线性表的结构分析和应用." 9787040461473: 16. 2017.1.1
[2] C语言实现顺序表(顺序存储结构) - 知乎 (zhihu.com)