声明:线性表的实现使用的是.cpp文件。
- 由于我使用的是vs2010,vs2010的编译器不支持函数返回值bool类型,并且不能这样定义for(int i= 0; i < 10; i++),必须在函数的开头声明i=0。为了编写的方便,我使用的是.cpp文件,没有使用.c文件。虽然是c++文件,但是内容是c语言。如果有需要.c文件的,请自行修改这两个地方就可以了。
注意:线性表的代码实现见博客https://blog.csdn.net/qq_43546676/article/details/105894787
1. 线性表的定义和基本操作
1.1 线性表的定义
线性表:是具有相同类型的n(n>=0)个元素的有限序列,其中n为表长。当n=0时,该表为空表。
线性表特点小结:
- 线性表是属于逻辑结构中线性结构的一种。
- 线性表中的元素类型相同。
- 线性表的大小是有限的。
- 表中的元素是有序的,一对一的关系。
1.2 线性表的基本操作
1.3 线性表的分类
* 按物理结构分
1. 线性表物理结构是顺序结构:称为顺序表
* 按数组是否动态分配
1. 数组动态分配的顺序表
2. 数组静态态分配的顺序表
2. 线性表物理结构是链式结构:称为链表
1. 单链表
2. 双向链表
3. 循环链表
1. 循环单链表
2. 循环双链表
* 注意:以上链表都可以分为带头结点和不带头结点两种。
2. 线性表的线性实现-顺序表
* 顺序表
* 优点:
1. 无须为表中数据元素间的逻辑关系而增加额外的存储空间。
2. 可以快速的根据下标存或取数据。
* 缺点:
1. 在前端插入和删除操作需要移动大量的元素
2. 当线性表长度变化较大时,难以确定存储空间的容量
3. 造成空间“碎片”。当线性表不存满时就存在碎片
由于顺序表的特性,使得我们可以使用数组来实现顺序表。但是根据数组的创建时间,我们可以将顺序表分为以下两种结构:
-
数组静态分配的顺序表
-
数组动态分配的顺序表
注意:数组静态分配的顺序表与数组动态分配的顺序表的差别主要在与前者是在定义顺序表时就定义了数组,相当于定死了顺序表的容量;而后者是在顺序表初始化时动态分配数组,数组存满后,又动态的分配一个空间更大的数组,并把之前数组中的元素复制过来,这样就解决了容量问题,可以动态扩充顺序表空间。
注意:可能有的小伙伴问为什么不直接用数组做为线性表?如果你有这个疑惑,可能你线性表的相关知识还没有理解透。
- 因为,线性表和数组处于不同的层次,线性表是一种抽象的数据结构,而数组是一种具体的数据结构。线性表是数据元素间是一对一的关系,他不管底层(物理结构上)是如何实现的。但是其底层不只是可以用数组顺序存储实现,也可以用链表链式存储实现。
2.1 数组静态态分配的顺序表
2.1.1 初始化和销毁
2.1.2 顺序表长度
2.1.3 添加元素
2.1.4 删除元素
2.1.5 修改元素
2.1.6 查找元素
2.1.7 打印顺序表
2.1.8 几个注意点
- 由于静态数组无法手动释放,所以在销毁顺序表的函数中,将顺序表长度置为0表示释放顺序表。
- 使用顺序表时,每声明一个顺序表都需要及时的初始化顺序表。(将length置为0)
- 元素的删除和添加时要及时的修改顺序表长度。
- 在进行元素的删除和添加时有一个小结论,顺序表需要移动的元素,是从元素移动的方向开始移动。
- 顺序表添加元素示意图:
- 顺序表删除元素示意图:
2.2 数组动态分配的顺序表
数组动态分配的顺序表主要解决了数组静态分配的顺序表的一个缺点:就是容量的问题,前者是可以动态扩充的,而后者是定义死的。实际上,前者更加贴切于线性表的定义。
2.2.1 初始化和销毁
2.2.2 顺序表长度
2.2.3 顺序表动态扩容
2.2.4 添加元素
2.2.5 删除元素
2.2.6 更改元素
2.2.7 查看元素
2.2.8 打印元素
2.2.9 几个注意点
1. 补充一些动态分配内存的一些知识。
1. 使用malloc()函数可以动态在堆中分配内存,这些动态分配的内存只有两种情况下被释放:
1. 手动使用free()函数释放。
2. 程序结束时才会释放。
2. free()函数用来手动释放堆内存,其有几个需要注意的地方:
1. free()函数只能释放动态分配的内存。如果是静态的,比如int a[2] = {1, 2, 3}, 就不能使用free(a)释放。
2. 如果将动态分配后地址的首地址给free(),他会将以这个地址为首的动态分配的地址全释放。
比如动态分配了一个数组 int * a = (int *)malloc(sizeof(int)*3); 使用free(a);
会将整个数组的内存全释放,而不是只释放一个单元。
2. 结构体变量间能够直接赋值,像基本类型一样。
ElemType a, b; //ElemType是结构体引用类型
a.age = 15;
a.name = "张三";
b = a; //可以这样
3. 使用malloc时,一定要注意后边是前边的整数倍。
数组动态分配的顺序表与数组静态分配的顺序表差别不大,主要是在初始化、顺序表的释放以及空间不足时,处理的方法不一样。其他都一样。
2.3 小结
3. 线性表的链式实现-链表
3.1 单链表
3.1.1 单链表的定义
1. 链表是指顺序表的物理结构上是链式存储。
2. 单链表是指链表只有一个指针域,且前一个结点的指针域指向后一个结点的地址。
3. 一般链表可以分为带头结点和不带头结点。带头结点主要是为了:
1. 链表的第一个位置和其他位置的操作统一。 一个是head->next = node; 一个是head = node;常规的结点都p = p->next
2. 空表和非空表的操作统一。
3.1.2 单链表的基本操作
基本操作:增删改查对我们来说不难,后面直接看代码就好了。需要提醒一下的就是前插与后插,这个在学习的过程中没怎么过多注意,但是考试中会考。所以,这里详细说一下:
注意:对链表的进行增加删除操作时,最好画个图,就能很快的知道怎么做。
3.1.2.1 已经给出i结点的地址,问前插与后插
在插入元素的时候,有头插、尾插、前插、后插。对于前插和后插,我们编程时算法复杂度是一样的。但是在做题的时候,比如:以知链表中结点i
的地址,对i
实现前插和后插可能直觉的觉得前插的算法复杂度是O(n),后插是O(1),其实两者都可以是O(1), 此时,前插可以转为后插。示意图如下:
3.1.2.2 已经给出i结点的地址,删除该节点
正常编程下,应该是遍历到i的前一个结点,这样算法复杂度是O(n)。在已知要删除结点的地址的情况下可以将算法复杂度降为O(1)。做法和上一题类似,也是交换元素:
3.1.3 单链表的实现
3.1.3.1 初始化和释放
3.1.3.2 添加元素:前插、后插、尾插
3.1.3.3 申请节点
3.1.3.4 删除节点
3.1.3.5 更改元素
3.1.3.6 查找元素
3.1.3.7 链表长度与判空
3.1.3.8 打印操作
3.1 4 小结
3.2 其他几种常用的链表
3.2.1 双链表
双链表和单链表操作差不多,特别需要注意的是:
在带头结点的双链表的尾部插入元素的步骤和其他任意位置插入的步骤不一样,因为,最后一个节点没有后继节点,所以不能进行后继节点的left操作。
其实也可以这样理解,把双链表看着两个方向想反的单链表,从左往右是带头结点,而从右往左是不带头结点。
删除结点的时候也是这种情况。
3.2.1.1 插入非最后一个结点
3.2.1.2 删除非最后一个结点
3.2.1.3 基本操作
3.2.1.3.1 初始化
3.2.1.3.2 插入
3.2.1.3.3 删除
3.2.1.3.4 遍历
3.2.1.4 小结
3.2.2 循环链表
循环单链表和单链表差不多,只是循环单链表的最后一个结点的指针指向头结点地址。
循环双向链表的头结点的左指针指向尾节点地址,尾结点的右指针指向头结点地址。
3.2.3 静态链表
静态链表的没有指针域,是下标域,存放的是下一个结点在SLinkList数组的下标。
3.3 链表的一些结论
- 所有链表
- 在插入节点时,先连接后面,再连接前面。
- 删除节点时,记得释放要删除的节点。
- 双链表
- 在插入和删除操作中,最后一个结点都要单独考虑。
4. 线性表的代码实现
注意:线性表的代码实现见博客https://blog.csdn.net/qq_43546676/article/details/105894787
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/84624.html