线性表的增删改查

什么是数据结构

数据结构,从名字上看是数据的结构,也就是数据的组织方式。

什么是线性表

线性表是 n 个数据元素的有限序列,最常用的是链式表达,通常也叫作线性链表或者链表。在链表中存储的数据元素也叫作结点,一个结点存储的就是一条数据记录。每个结点的结构包括两个部分:

  1. 具体的数据值。
  2. 指向下一个节点的指针。

在这里插入图片描述
在链表的最前面,通常会有个头指针用来指向第一个结点。对于链表的最后一个结点,由于在它之后没有下一个结点,因此它的指针是个空指针。
在这里插入图片描述
仔细观察上图,你会发现这个链表只能通过上一个结点的指针找到下一个结点,反过来则是行不通的。因此,这样的链表也被称作单向链表。

有时候为了弥补单向链表的不足,我们可以对结点的结构进行改造:

  • 对于一个单向链表,让最后一个元素的指针指向第一个元素,就得到了循环链表;
  • 或者把结点的结构进行改造,除了有指向下一个结点的指针以外,再增加一个指向上一个结点的指针。这样就得到了双向链表。
    在这里插入图片描述

线性表对数据的增删改查

学会了线性表原理之后,我们就来围绕数据的增删查操作,来看看线性表的表现。

首先看一下增加操作。如下有一个链表,它存储了 10 个同学的考试成绩。现在发现这样的问题,在这个链表中,有一个同学的成绩忘了被存储进去。假设我们要把这个成绩在红色的结点之后插入,那么该如何进行呢?

其实,链表在执行数据新增的时候非常容易,只需要把待插入结点的指针指向原指针的目标,把原来的指针指向待插入的结点,就可以了。如下图所示:
在这里插入图片描述
代码如下:

s.next = p.next;
p.next = s;

接下来我们看一下删除操作。还是这个存储了同学们考试成绩的链表,假设里面有一个成绩的样本是被误操作放进来的,我们需要把这个样本删除。链表的删除操作跟新增操作一样,都是非常简单的。如果待删除的结点为 b,那么只需要把指向 b 的指针 (p.next),指向 b 的指针指向的结点(p.next.next)。如下图所示:
在这里插入图片描述
代码如下:

p.next = p.next.next;

最后,我们再来看看查找操作。我们在前面的课时中提到过,查找操作有两种情况:

  • 第一种情况是按照位置序号来查找。

它和数组中的 index 是非常类似的。假设一个链表中,按照学号存储了 10 个同学的考试成绩。现在要查找出学号等于 5 的同学,他的考试成绩是多少,该怎么办呢?

其实,链表的查找功能是比较弱的,对于这个查找问题,唯一的办法就是一个一个地遍历去查找。也就是,从头开始,先找到学号为 1 的同学,再经过他跳转到学号为 2 的同学。直到经过多次跳转,找到了学号为 5 的同学,才能取出这个同学的成绩。如下图所示:
在这里插入图片描述

  • 第二种情况是按照具体的成绩来查找。

同样,假设在一个链表中,存储了 10 个同学的考试成绩。现在要查找出是否有人得分为 95 分。链表的价值在于用指针按照顺序连接了数据结点,但对于每个结点的数值则没有任何整合。当需要按照数值的条件进行查找时,除了按照先后顺序进行遍历,别无他法。

因此,解决方案是,判断第一个结点的值是否等于 95:

  • 如果是,则返回有人得分为 95 分;
  • 如果不是,则需要通过指针去判断下一个结点的值是否等于 95。以此类推,直到把所有结点都访问完。

在这里插入图片描述
在这里插入图片描述
根据这里的分析不难发现,链表在新增、删除数据都比较容易,可以在 O(1) 的时间复杂度内完成。但对于查找,不管是按照位置的查找还是按照数值条件的查找,都需要对全部数据进行遍历。这显然就是 O(n) 的时间复杂度。

虽然链表在新增和删除数据上有优势,但仔细思考就会发现,这个优势并不实用。这主要是因为,在新增数据时,通常会伴随一个查找的动作。例如,在第五个结点后,新增一个新的数据结点,那么执行的操作就包含两个步骤:

  • 第一步,查找第五个结点;
  • 第二步,再新增一个数据结点。整体的复杂度就是 O(n) + O(1)。

这等同于 O(n) 的时间复杂度。线性表真正的价值在于,它对数据的存储方式是按照顺序的存储。如果数据的元素个数不确定,且需要经常进行数据的新增和删除时,那么链表会比较合适。如果数据元素大小确定,删除插入的操作并不多,那么数组可能更适合些。

总结

本博客的内容主要围绕线性表的原理、线性表对于数据的增删查操作展开。线性链表结构的每个结点,由数据的数值和指向下一个元素的指针构成。根据结构组合方式的不同,除了单向链表以外,还有双向链表、循环链表以及双向循环链表等变形。

经过我们的分析,链表在增、删方面比较容易实现,可以在 O(1) 的时间复杂度内完成。但对于查找,不管是按照位置的查找还是按照数值条件的查找,都需要对全部数据进行遍历。

线性表的价值在于,它对数据的存储方式是按照顺序的存储。当数据的元素个数不确定,且需要经常进行数据的新增和删除时,那么链表会比较合适。链表的翻转、快慢指针的方法,是你必须掌握的内容。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这里提供一个基于数组实现的线性增删的C语言代码示例: ```c #include <stdio.h> #define MAX_SIZE 100 // 线性的最大长度 int list[MAX_SIZE]; // 线性数组 int length = 0; // 线性当前长度 /* 在线性末尾插入元素 */ void insert(int value) { if (length >= MAX_SIZE) { printf("Error: Linear list is full!\n"); return; } list[length++] = value; } /* 删除线性中指定位置的元素 */ void delete(int index) { if (index < 0 || index >= length) { printf("Error: Index out of range!\n"); return; } for (int i = index; i < length - 1; i++) { list[i] = list[i+1]; } length--; } /* 修线性中指定位置的元素 */ void modify(int index, int value) { if (index < 0 || index >= length) { printf("Error: Index out of range!\n"); return; } list[index] = value; } /* 线性中第一个值为value的元素的位置,如果没有则返回-1 */ int search(int value) { for (int i = 0; i < length; i++) { if (list[i] == value) { return i; } } return -1; } /* 输出线性中的所有元素 */ void print() { printf("Linear list: "); for (int i = 0; i < length; i++) { printf("%d ", list[i]); } printf("\n"); } int main() { insert(1); insert(2); insert(3); print(); // Linear list: 1 2 3 delete(1); print(); // Linear list: 1 3 modify(1, 4); print(); // Linear list: 1 4 int index = search(4); if (index != -1) { printf("Index of 4: %d\n", index); // Index of 4: 1 } else { printf("4 not found!\n"); } return 0; } ``` 以上是一个简单的线性增删的C语言代码示例,仅供参考。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值