第二章 线性表
22.1 线性表的定义和基本操作
- 线性表(Linear List)的定义
- 线性表的基本操作
什么时候要传入参数的引用 **“&” **—— 对参数的修改结果需要 “带回来”
2.2 线性表的顺序表示
2.2.1 顺序表的定义
- 顺序表的实现——静态分布
- 顺序表的实现——动态分配
顺序表特点
2.2.2 顺序表上基本操作的实现
顺序表的插入和删除
- 顺序表的插入
静态分配方法(动态类似):
插入操作的时间复杂度:
- 顺序表的删除
删除操作:
删除操作的时间复杂度:
顺序表的查找
- 按位查找
GetElem(L,i):按位查找操作。获取表L中 第i个位置 的元素的值。
1)静态分配:
2)动态分配:
其中:
时间复杂度:
- 按值查找
LocateElem(L,e):按值查找操作。在表L中查找具有给定关键字值的元素。
数组下标 为i的元素值等于e,返回其 位序i+1
关于结构体的比较:
Tips:
《数据结构》考研初试中,手写代码可以直接用“==”,无论 ElemType 是基本数据类型还是结构类型。手写代码主要考察学生是否能理解算法思想,不会严格要求代码完全可运行。
时间复杂度:
2.3 线性表的链式表示
2.3.1 单链表的定义
- 什么是单链表
- 用代码定义一个单链表
🎈 结点的表示:
即 typedef struct LNode LNode
后
struct LNode *p = (struct LNode*)malloc(sizeof(struct LNode));
可写为:
typedef struct LNode LNode
LNode *p = (LNode*)malloc(sizeof(LNode));
🎈 定义一个单链表:
获取第 i 位结点(带头结点):
头插法建立单链表的算法:
- 两种实现方式
🎨 不带头结点
🎨 带头结点
🎨 不带头结点 V.S. 带头结点
2.3.2 单链表上基本操作的实现
单链表的插入和删除
插入
- 按位序插入
🎨 带头结点:
最好时间复杂度:O( 1 )
最坏时间复杂度:O( n )
平均时间复杂度:O( n )
🎨 不带头结点:
除非特别声明,之后的代码默认带头结点
结论:不带头结点写代码更不方便,推荐用带头结点
注意:考试中带头、不带头都有可能考察,注意审题
- 指定结点的后插操作
封装一个后插操作:
后插结点:
- 指定结点的前插操作
前插操作:
删除
- 按位序删除(带头结点)
- 指定结点的删除
此段代码有缺点,若p是最后一个结点,p -> data = p -> next -> data;
这句会报错:
封装的好处:小功能模块化,代码逻辑清晰
单链表的查找
GetElem(L,i):按位查找操作。获取表L中第i个位置的元素的值。
LocateElem(L,e):按值查找操作。在表L中查找具有给定关键字值的元素。
- 按位查找
平均时间复杂度:O( n )
前面插入、删除操作的函数:
封装(基本操作)的好处:避免重复代码,简洁、易维护
- 按值查找
- 求表的长度
单链表建立
初始化单链表操作:
- 尾插法建立单链表
如果借助之前的插入操作函数进行尾插,也可以实现,但由于每次从头开始遍历,时间复杂度为O( n² ):
设置一个指针r+后插:
- 头插法建立单链表
2.3.3 双链表
- 初始化(带头结点)
- 双链表的插入
- 双链表的删除
- 双链表的遍历
2.3.4 循环链表
- 循环单链表
比较:
- 循环双链表
双链表的插入:
双链表的删除:
2.3.5 静态链表
- 什么是静态链表
- 代码定义:
使用了typedef
:
- 简述基本操作的实现
初始化静态链表:
把 a[0] 的 next 设为 -1
把其他结点的 next 设为一个特殊值用来表示结点空闲,如 -2
其他:
2.3.6 顺序表和链表的比较
- Round1:逻辑结构
都属于线性表,都是线性结构
- Round2:物理结构 / 存储结构
- Round3:基本操作
创:
销:
增删:
查:
用顺序表or链表: