mirror of
https://github.com/142vip/408CSFamily.git
synced 2026-06-15 06:26:35 +08:00
update
This commit is contained in:
BIN
数据结构/.DS_Store
vendored
BIN
数据结构/.DS_Store
vendored
Binary file not shown.
BIN
数据结构/思维导图/.DS_Store
vendored
Normal file
BIN
数据结构/思维导图/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
数据结构/思维导图/基础入门.xmind
Normal file
BIN
数据结构/思维导图/基础入门.xmind
Normal file
Binary file not shown.
BIN
数据结构/思维导图/线性表.xmind
Normal file
BIN
数据结构/思维导图/线性表.xmind
Normal file
Binary file not shown.
BIN
数据结构/数据结构.xmind
BIN
数据结构/数据结构.xmind
Binary file not shown.
BIN
数据结构/线性表/.DS_Store
vendored
Normal file
BIN
数据结构/线性表/.DS_Store
vendored
Normal file
Binary file not shown.
61
数据结构/线性表/1.基础概念和基本操作.md
Normal file
61
数据结构/线性表/1.基础概念和基本操作.md
Normal file
@@ -0,0 +1,61 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2021-03-07 21:57:27
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2021-03-07 21:57:35
|
||||
-->
|
||||
|
||||
|
||||
|
||||
## 线性表的基础概念和基本操作
|
||||
|
||||
> 强调线性表是一种逻辑结构,不是存储结构
|
||||
|
||||
|
||||
### 定义
|
||||
|
||||
线性表是具有相同数据类型的n(n≥0)个数据元素的有限序列。一般表示:
|
||||
|
||||
L=(a<sub>1</sub>,a<sub>2</sub>,a<sub>3</sub>......a<sub>n</sub>) 其中n可以理解为表长(线性表的长度),n=0时候,即表空
|
||||
|
||||
|
||||
- `表头元素`:线性表中唯一的“第一个”数据元素,例如a<sub>1</sub>
|
||||
- `表尾元素`:线性表中唯一的“最后一个”数据元素,例如a<sub>n</sub>
|
||||
|
||||
|
||||
重要逻辑特性:
|
||||
|
||||
- 除表头元素外,线性表中每个元素有且仅有一个`直接前驱`
|
||||
- 除表尾元素外,线性表中每个元素有且仅有一个`直接后继`
|
||||
|
||||
基于此,这种**线性有序的逻辑结构**,使得线性表的特点如下:
|
||||
|
||||
- 元素的**个数有限**(强调有限序列)
|
||||
- 元素在逻辑上具有**顺序性**,在序列中每个元素都是都有先后次序的
|
||||
- 元素都数据元素,**每个元素都是单个元素**
|
||||
- 元素的**数据类型都相同**(强调相同数据类型),每个数据元素占用相同大小的存储空间
|
||||
- 元素具有**抽象性**,仅仅讨论元素之间的逻辑关系,不需要去考虑元素究竟表示的什么内容
|
||||
|
||||
|
||||
> Tips: **线性表是一种逻辑结构**,表示元素之间一对一的相邻关系。**顺序表和链表则指的是存储结构**
|
||||
|
||||
|
||||
|
||||
### 基本操作
|
||||
|
||||
- `InitList(&L)`: **初始化表**。构造空的线性表
|
||||
- `Length(L)`:**获取表的长度**。返回线性表L的长度,即表中的数据元素个数
|
||||
- `LocateElem(L,e)`:**按值查找操作**。在表L中国查找具有给定关键字的元素
|
||||
- `GetElem(L,i)`:**按位查找操作**。获取表中第i个位置的元素的值
|
||||
- `ListInsert(&L,i,e)`:**插入操作**。在表的第i个位置上插入指定元素e
|
||||
- `ListDelete(&L,i,&e)`:**删除操作**。删除表中第i个位置的元素,并用e返回删除元素的值
|
||||
- `PrintList(L)`:**输出操作**。按照前后顺序(如:1、2....n)输出线性表的所有元素值
|
||||
- `Empty(L)`:**判空操作**。当表L为空,则返回true,否则返回false
|
||||
- `DestoryList(&L)`:**销毁操作**。将线性表销毁,释放线性表L所占用的内存空间(类似:释放内存)
|
||||
|
||||
|
||||
线性表是具有相同的数据类型的有限个数据元素组成的,**数据元素是由数据项组成的**
|
||||
|
||||
|
||||
274
数据结构/线性表/2.线性表的顺序表示.md
Normal file
274
数据结构/线性表/2.线性表的顺序表示.md
Normal file
@@ -0,0 +1,274 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2021-03-07 21:57:52
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2021-03-07 21:58:00
|
||||
-->
|
||||
|
||||
|
||||
## 线性表的顺序表示
|
||||
|
||||
|
||||
### 定义
|
||||
|
||||
`顺序表`:顺序存储的线性表,**是用一组地址连续的存储单元,依次存储线性表中的数据元素,使得在逻辑上相邻的两个元素在物理位置上也相邻。**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
顺序表中的元素的逻辑顺序与实际的物理位置相同
|
||||
|
||||
|
||||
|
||||
注意:
|
||||
|
||||
- 线性表中的元素的位序是从1开始的,例如1、2、3...
|
||||
- 数组中的元素的下标是从0开始的,例如0、1、2...
|
||||
|
||||
```c
|
||||
# define MaxSize 20 // 定义常量MaxSize 用来声明顺序表的最大长度
|
||||
|
||||
// 线性表结构体定义【ElemType用来代指顺序表中元素的类型,例如高级语言中的int、string....】
|
||||
typedef struct{
|
||||
ElemType data[MaxSize]; // 顺序表的元素
|
||||
int length; // 顺序表的长度
|
||||
}SqList
|
||||
|
||||
```
|
||||
|
||||
#### 存储分配
|
||||
|
||||
`静态分配`:数组的大小和空间都是实现确定好的,一旦存储空间占满就会产生溢出,直接导致程序崩溃。(有点内存不够,宕机重启的意思....)
|
||||
|
||||
`动态分配`:存储数据的空间在程序执行过程中通过`动态存储分配语句`分配的,即便是数据空间占满,也可以另外开辟一块更大的空间,来替换原来的存储空间,满足扩充数据空间的目的。(有点动态规划的意思....)最重要的是:**不需要像静态分配那样,一次性地固定线性表的空间和大小**
|
||||
|
||||
|
||||
|
||||
```c
|
||||
#define InitSize 100 // 表长度初始化
|
||||
|
||||
|
||||
// 动态分配数组顺序表的结构体定义
|
||||
typedef struct{
|
||||
ElemType *data; // 动态分配数组的指针
|
||||
int MaxSize,length; // 数组的最大容量和当前元素个数
|
||||
}SqList;
|
||||
|
||||
```
|
||||
|
||||
动态分配语句
|
||||
|
||||
```C
|
||||
// C语言中
|
||||
|
||||
L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize);
|
||||
|
||||
|
||||
// C++ 中
|
||||
|
||||
L.data=new ElemType[InitSize];
|
||||
|
||||
```
|
||||
|
||||
`malloc()函数`: 指针型函数,返回的指针指向该分配域的开头的位置。作用是在内存的动态存储区中分配一个长度为size的连续空间。[百度百科](https://baike.baidu.com/item/malloc%E5%87%BD%E6%95%B0/8582146?fr=aladdin)
|
||||
|
||||
**动态分配不是链式存储,而是属于顺序存储结构**,动态分配的物理结构没有改变,依然是随机存取的方式。只是分配的空间大小可以在运行时决定;
|
||||
|
||||
|
||||
#### 顺序表的特点
|
||||
|
||||
|
||||
- 随机访问【这是最主要的特点】,通过存储起始地址和元素序号O(1)时间内访问指定元素。
|
||||
- 存储密度高,没有结点只存储数据元素,不像索引存储那样,还需要索引表什么的..
|
||||
- 逻辑上相邻的元素物理上也相邻,插入和删除需要移动大量元素
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 基本操作
|
||||
|
||||
|
||||
#### 插入
|
||||
|
||||
在顺序表L的第i(1≤i≤L.length+1)个位置插入新的元素e
|
||||
|
||||
- 第一步:如果i非法,则直接返回false,插入失败,结束插入过程
|
||||
- 第二步:i正常,将表的第i个元素以及后面的所有元素都像有移动一个位置,在腾出来的空位置插入元素e
|
||||
- 第三步:顺序表插入成功,返回true
|
||||
|
||||
注意:先判空和临界值,提高算法健壮性
|
||||
|
||||
```C++
|
||||
|
||||
/*
|
||||
* @Description: 顺序表的插入操作
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2020-02-23 07:48:26
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2020-02-23 07:48:26
|
||||
*/
|
||||
bool ListInsert(SqList &L, int i, ElemType e){
|
||||
|
||||
// i非法 i=1 表头 i=L.length+1 表尾巴
|
||||
if(i<1||i>L.length+1){
|
||||
return false;
|
||||
}
|
||||
|
||||
// 存储空间满,无法插入
|
||||
if(L.length >= MaxSize){
|
||||
return false;
|
||||
}
|
||||
|
||||
// 遍历,将位置元素往后移动,注意从后往前循环,避免值被覆盖
|
||||
for(int j=L.length; j>=i;j--){
|
||||
L.data[j]=L.data[j-1];
|
||||
}
|
||||
|
||||
// 此时,表L中的第i个元素和第i+1元素素值一样,将新元素存入i位置即可
|
||||
|
||||
// 第i个元素,对应的位置角标为i-1
|
||||
L.data[i-1]=e;
|
||||
|
||||
// 表长度加1
|
||||
L.length++;
|
||||
|
||||
// 返回插入成功
|
||||
return true;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
注意:区别顺序表中的位序和角标;
|
||||
|
||||
**时间复杂度**
|
||||
|
||||
- 最好情况:在表尾插入,元素向后移动循环没有执行,时间复杂度O(1);
|
||||
- 最坏情况:在表头插入,元素后移循环执行n次,时间复杂度为O(n);
|
||||
- 平均情况:随机插入,平均次数为:n/2,对应的平均复杂度为O(n);
|
||||
|
||||
|
||||
**线性表插入算法的平均时间复杂度为:O(n)**
|
||||
|
||||
> Tips: 需要根据实现代码理解循环为什么是从后往前来实现元素后移,通过for循环可以很明显的看出表尾插入快,表头插入慢
|
||||
|
||||
#### 删除
|
||||
|
||||
删除顺序表L中第i(1≤i≤L.length+1)个位置的元素
|
||||
|
||||
- 成功,返回true,将被删除的元素用引用变量返回;
|
||||
- 失败,返回false
|
||||
|
||||
```C++
|
||||
|
||||
/*
|
||||
* @Description: 顺序表的删除操作
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2020-02-23 07:48:26
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2020-02-23 07:48:26
|
||||
*/
|
||||
bool ListDelete(SqList &L, int i, ElemType &e){
|
||||
|
||||
// i非法 i=1 表头 i=L.length+1 表尾巴
|
||||
if(i<1||i>L.length+1){
|
||||
return false;
|
||||
}
|
||||
|
||||
// 存储空间满,无法插入
|
||||
if(L.length >= MaxSize){
|
||||
return false;
|
||||
}
|
||||
|
||||
// 引用变量e赋值
|
||||
e=L.data[i-1]
|
||||
|
||||
// 遍历,第i个元素后面的往前移动
|
||||
for(int j=i; j<=L.length;j++){
|
||||
// 从第i个元素开始,角标从i-1开始
|
||||
L.data[j-1]=L.data[j];
|
||||
}
|
||||
|
||||
// 此时,表L中的表尾元素和倒数第二个元素值一样,将表的长度-1
|
||||
|
||||
// 表长度减1
|
||||
L.length--;
|
||||
|
||||
// 返回删除成功
|
||||
return true;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
从这里来看,删除、插入元素都会涉及到大量的元素的移动(最好情况例外),总结而言:
|
||||
- 元素从后往前移,循环从前往后遍历
|
||||
- 元素从前往后移,循环从后往前遍历
|
||||
|
||||
|
||||
**时间复杂度:**
|
||||
|
||||
|
||||
- 最好情况:删除表尾元素,不需要移动任何元素,时间复杂度为O(1);
|
||||
- 最坏情况:删除表头元素,需要移动除第一个元素外的所有元素,时间复杂度为O(n);
|
||||
- 平均情况:随机删除,平均需要(n-1)/2,对应的时间复杂度为O(n);
|
||||
|
||||
|
||||
|
||||
**线性表删除算法的平均时间复杂度为O(n);**
|
||||
|
||||
|
||||
#### 按值查找(顺序查找)
|
||||
|
||||
在顺序表L中查找第一个元素值等于e的元素,并返回位序
|
||||
|
||||
|
||||
```C++
|
||||
/*
|
||||
* @Description: 顺序表的按值查找(顺序查找)
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2020-02-23 07:48:26
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2020-02-23 07:48:26
|
||||
*/
|
||||
int LocateElem(SqList L,ElemType e){
|
||||
int i;
|
||||
// 循环判断
|
||||
for(i=0;i<L.length;i++){
|
||||
if(L.data[i]===e){
|
||||
// i是元素的角标,i+1是具体元素的位序号
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
|
||||
// 未命中,返回0,即:没有
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
注意理解`位序`的含义,即元素在线性表中的位置序号,角标为`i`(角标从0开始),对应的位序为`i+1`(位序从1开始)。当返回为0时,则直接代表没有`命中`;
|
||||
|
||||
|
||||
**时间复杂度:**
|
||||
|
||||
- 最好情况:查找的元素在表头,只需要比较一次,循环成本最小,时间复杂度为O(1);
|
||||
- 最坏情况:查找的元素在表尾或者不存在,需要完整遍历,比较n次,时间复杂度为O(n);
|
||||
- 平均情况:随机查找表上的第i个(1≤i≤L.length)元素,平均次数为(n+1)/2,对应时间复杂度为O(n)
|
||||
|
||||
|
||||
|
||||
**线性表按值查找(顺序查找)的平均时间复杂度为O(n);**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**顺序存取是读写方式,不是存储结构;顺序存储是存储结构,包括有:顺序存储、链式存储、索引存储、散列存储**
|
||||
440
数据结构/线性表/3.线性表的链式表示.md
Normal file
440
数据结构/线性表/3.线性表的链式表示.md
Normal file
@@ -0,0 +1,440 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2021-03-07 21:58:40
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2021-03-07 21:58:40
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
## 线性表的链式表示
|
||||
|
||||
顺序表的插入、删除操作需要移动大量元素,影响了运行效率(虽然时间复杂度为O(1)的情况也存在)。
|
||||
|
||||
|
||||
**链式存储线性表时,不需要使用连续的存储单元,不要求逻辑上相邻的两个元素在物理位置上也相邻**
|
||||
|
||||
|
||||
理解“链”的含义,链条--->捆绑、指向------>指针
|
||||
|
||||
|
||||
**链式存储线性表时,对线性表的插入、删除元素是不需要移动元素的,只是需要修改指针**
|
||||
|
||||
|
||||
- 单链表
|
||||
- 双链表
|
||||
- 循环链表
|
||||
- 静态链表
|
||||
|
||||
|
||||
|
||||
### 单链表
|
||||
|
||||
线性表的链式存储称作`单链表`,通过**一组任意的存储单元**来存储线性表中的数据元素。
|
||||
|
||||
|
||||
每个链表结点(node)除了存放元素自身的信息外,还需要存放一个指向其后继结点的指针。目的是:**通过指针建立起链表元素之间的线性关系**
|
||||
|
||||
|
||||
单链表中结点类型的描述:
|
||||
```C++
|
||||
|
||||
// 单链表结点类型定义
|
||||
typeof struct LNode{
|
||||
ElemType data; // 数据域
|
||||
struct LNode *next; // 指针域
|
||||
}LNode , *LinkList;
|
||||
|
||||
```
|
||||
|
||||
|
||||
单链表可以解决顺序表需要大量连续存储空间的缺点,但是单链表在数据域的基础上附加了指针域,存在浪费存储空间的缺点;
|
||||
|
||||
|
||||
单链表的元素是**离散地分布**在存储空间中的,因此**单链表是非随机存取的存储结构**,不能直接找到表中特定的结点,需要从头开始遍历、一次查找;
|
||||
|
||||
|
||||
通常,**头指针用来标识一个单链表**。头指针指向`NULL`时,标识单链表为空。
|
||||
|
||||
|
||||
|
||||
#### 头结点
|
||||
|
||||
为了操作上的方便,在单链表第一个结点之前附加一个结点,叫做**头结点**。
|
||||
|
||||
- 头结点的数据域可以不存任何信息、也可以记录表长等基础信息
|
||||
- 头结点的指针域指向线性表的第一个元素结点;
|
||||
|
||||
|
||||
|
||||
|
||||
**不论单链表是否带头结点(可选),头指针始终指向链表的第一个结点。**
|
||||
|
||||
头结点是带头结点的链表中的第一个结点【重要】
|
||||
|
||||
- 头结点的数据域可以不存任何信息、也可以记录表长等基础信息
|
||||
- 头结点的指针域指向线性表的第一个元素结点;
|
||||
|
||||
|
||||
头结点的优点:
|
||||
|
||||
- 因为开始结点的位置被存放在头结点的指针域中,所以在链表的第一个位置上的操作和在表的其他位置上的操作一致,不需要进行特殊处理;
|
||||
- 无论链表是否为空,头指针始终是指向头结点的头结点的非空指针【空表中,往往就只有头结点,此时头结点的指针域为空,可以有效避免头指针空指针异常问题】-----> **头结点的引入,很好的统一了空表和非空表的操作;**
|
||||
|
||||
|
||||
|
||||
#### 头插法
|
||||
|
||||
> 从空表开始,生成新的结点,将读取的数据存放在新结点的数据域中,将新结点插入到当前链表的表头【头结点之后】
|
||||
|
||||
|
||||
```C++
|
||||
/*
|
||||
* @Description: 单链表头插法创建
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2020-03-04 23:38:04
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2020-03-04 23:39:16
|
||||
*/
|
||||
LinkList CreateListWithStartNode(LinkList &L){
|
||||
|
||||
LNode *s;
|
||||
int x;
|
||||
L=(LinkList)malloc(sizeof(LNode)); // 创建头结点L
|
||||
L->next=NULL; // 初始化空链表
|
||||
|
||||
// 控制台输入值
|
||||
scanf("%d",&x);
|
||||
|
||||
// 输入9999 表示结束
|
||||
while(x!==9999){
|
||||
// 开辟新结点存储空间
|
||||
s=(LNode*)malloc(sizeof(LNode));
|
||||
// 结点数据域赋值
|
||||
s->data=x;
|
||||
// 修改指针,新结点插入表中【注意:L->next为头结点的指针域】
|
||||
s->next=L->next;
|
||||
L->next=s;
|
||||
scanf("%d",&x);
|
||||
}
|
||||
|
||||
// 返回单链表
|
||||
return L;
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
特点:
|
||||
|
||||
- 读入数据的顺序与生成的链表中的元素顺序是相反的【结合队列先进先出思考】
|
||||
- 每个结点插入的时间复杂度为O(1),单链表长度为n时,头插法的时间复杂度为O(n)【结合算法中的while循环,可以很明确看出时间复杂度】
|
||||
|
||||
|
||||
#### 尾插法
|
||||
|
||||
头插法建立的单链表,链表中结点的次序和输入数据的顺序不一致【相反】,尾插法则很好的避免了这个问题;
|
||||
|
||||
>新结点插入到当前链表的表尾上,必须增加一个尾指针r,始终指向当前链表的尾结点;
|
||||
|
||||
|
||||
```C++
|
||||
|
||||
/*
|
||||
* @Description: 单链表尾插法创建
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2020-03-04 23:38:04
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2020-03-04 23:39:16
|
||||
*/
|
||||
LinkList CreateListWithEndNode(LinkList &L){
|
||||
|
||||
|
||||
int x; // 输入结点值
|
||||
L=(LinkList)malloc(sizeof(LNode));
|
||||
LNode *s; // 新结点s
|
||||
LNode *r=L; // r为尾指针
|
||||
|
||||
// 控制台输入值
|
||||
scanf("%d",&x);
|
||||
|
||||
while(x!==9999){
|
||||
// 开辟新结点存储空间
|
||||
s=(LNode *)malloc(sizeof(LNode));
|
||||
|
||||
// 新结点s的数据域赋值为x
|
||||
s->data=x;
|
||||
// 单链表L的尾指针指向新的结点s
|
||||
r->next=s;
|
||||
|
||||
// 指针r指向新的表尾结点
|
||||
r=s;
|
||||
|
||||
scanf("%d",&x);
|
||||
}
|
||||
|
||||
// 表尾指针置空【重要】
|
||||
r->next=NULL;
|
||||
|
||||
// 返回单链表
|
||||
return L;
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
特点:
|
||||
|
||||
- 读入数据的顺序与生成的链表中的元素顺序完全一致
|
||||
- 每个结点插入的时间复杂度为O(1),单链表长度为n时,尾巴插法的时间复杂度为O(n)【结合算法中的while循环,可以很明确看出时间复杂度】
|
||||
- 相比头插法附设了一个指向表尾结点的指针,但时间复杂度与头插法相同
|
||||
|
||||
|
||||
|
||||
#### 按序号查找
|
||||
|
||||
> 在单链表中从第一个结点出发,顺指针next域逐个往下搜索、遍历,直到找出第i个结点为止,否则返回最后一个结点指针域NULL
|
||||
|
||||
|
||||
```C++
|
||||
|
||||
/*
|
||||
* @Description: 单链表按序号查找
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2020-03-04 23:38:04
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2020-03-04 23:39:16
|
||||
*/
|
||||
LNode *GetElem(LinkList L,int i){
|
||||
int j=1; // 查询计数,初始为1
|
||||
LNode *p=L->next; // 单链表头结点指针赋值给指针p
|
||||
|
||||
|
||||
// 第0个元素,则指向头结点,返回头结点
|
||||
if(i==0){
|
||||
// 头结点包含数据域和指针域
|
||||
return L;
|
||||
}
|
||||
|
||||
// 不等于0,却小于1,则i为负数无效,直接返回NULL,查询结果空;
|
||||
if(i<1){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// p存在且计数没有走到初始i的位置
|
||||
while(p&&j<i){
|
||||
|
||||
// 指针后移
|
||||
p=p->next;
|
||||
|
||||
// 计数标记+1
|
||||
j++;
|
||||
}
|
||||
|
||||
// 注意: 当p不存在时, 跳出循环,p=NULL; 当p存在但是j大于等于i,跳出循环,返回查找的结果,返回p
|
||||
// 从跳出循环上来分析,p要么存在即:找到的结点元素,要么为空即NULL
|
||||
|
||||
// 跳出循环,返回第i个结点的指针
|
||||
return p;
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
需要遍历(扫描)单链表,时间复杂度为:O(n)
|
||||
|
||||
|
||||
|
||||
#### 按值查找
|
||||
|
||||
> 从单链表的第一个结点开始,从前往后依次比较表中个结点数据域的值,等于给定值e,则返回该结点的指针;若整个单链表【遍历完】中没有数据域值为e的结点,则返回NULL;
|
||||
|
||||
|
||||
```C++
|
||||
|
||||
/*
|
||||
* @Description: 单链表按值查找
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2020-03-04 23:38:04
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2020-03-04 23:39:16
|
||||
*/
|
||||
LNode *LocateElem(LinkList L,ElemType e){
|
||||
|
||||
// 指针【哨兵】
|
||||
LNode *p=L->next;
|
||||
// 从第1个结点开始查找数据域(data)为e的结点
|
||||
while(p!=NULL&&p->data!=e){
|
||||
// 无法匹配,指针后移
|
||||
p=p->next;
|
||||
}
|
||||
|
||||
// 注意:p为NULL的时候,说明单链表已经遍历的尾结点了,跳出循环,没有找到目标结点;
|
||||
|
||||
// 查找到第1个匹配的结点,跳出循环,返回结点指针
|
||||
return p;
|
||||
//
|
||||
}
|
||||
|
||||
```
|
||||
链表遍历无法匹配,会返回NULL,因为在尾结点无法匹配的时候,直接返回尾结点指针域
|
||||
|
||||
需要遍历(扫描)单链表,时间复杂度为:O(n)
|
||||
|
||||
|
||||
#### 结点插入
|
||||
|
||||
> 单链表中,将值为x的新结点插入到单链表的第i个位置上
|
||||
|
||||
- 第一步: 检查插入位置的合法性;
|
||||
- 第二步: 找到待插入位置的前驱结点,即第(i-1)个结点;
|
||||
- 第三部: 在前驱结点后插入新结点;
|
||||
|
||||
```C++
|
||||
// 循环遍历,时间复杂度O(n)
|
||||
p=GetElem(L,i-1);
|
||||
|
||||
// 移动指针,时间复杂度O(1)
|
||||
s->next=p->next;
|
||||
p->next=s;
|
||||
```
|
||||
|
||||
结合上面的代码可以看出,将元素x插入到单链表L的第i个元素上,必须先找到单链表L的i个结点的前驱结点,即(i-1)的位置,需要采用`GetElem()`函数,按照序号查找;
|
||||
|
||||
如果返回的前驱结点不为空,则说明插入的位置i合法,否则位置非法,插入失败;
|
||||
|
||||
找到前驱结点p后,最重要的是移动指针,将新的结点s的指针域指向结点p的指针域,也就是s的指针域指向元素p的后继结点,第i个结点元素
|
||||
|
||||
原来的(i-1)位置上的元素,也就是前驱结点p的指针域则必须指向新的结点元素;
|
||||
|
||||
**上面的过程不能更换,避免后继指针不存在的问题**
|
||||
|
||||
|
||||
最后的最后,一定要注意将s的数据域赋值x
|
||||
|
||||
|
||||
插入结点的时间复杂度集中在查找第(i-1)个元素,时间复杂度为O(n);如果在给定结点的后面插入新结点,只需要执行`p->next=s`操作,时间复杂度为O(1)
|
||||
|
||||
|
||||
|
||||
##### 前插操作
|
||||
|
||||
> 在某结点的前面插入一个新的结点
|
||||
|
||||
**对结点的前插操作都可以转化为后插操作,前提:需要从单链表的头结点开始顺序查找到其前驱结点;时间复杂度为O(n)。**
|
||||
|
||||
##### 后插操作
|
||||
|
||||
> 在某结点的后面插入一个新的结点,单链表插入算法中,通常采用后插操作的
|
||||
|
||||
|
||||
```C++
|
||||
|
||||
// 结点s插入到结点p的前面,修改指针域,顺序不能改变
|
||||
s->next=p->next;
|
||||
p->next=s;
|
||||
|
||||
|
||||
// 经典的借助变量,进行值交换
|
||||
temp=p->data;
|
||||
p->data=s->data;
|
||||
s->data=temp;
|
||||
|
||||
```
|
||||
|
||||
上述借助临时变量`temp`来将结点s和结点p的数据域进行交换,需要开辟O(1)的空间复杂度,但是时间复杂度却从O(n)改变为O(1),典型的空间换时间策略
|
||||
|
||||
|
||||
#### 删除结点
|
||||
|
||||
> 将单链表L的第i个结点元素删除;
|
||||
|
||||
|
||||
- 第一步: 先检查删除位置的合法性;
|
||||
- 第二步: 查找表中的第(i-1)个结点,即被删结点的前驱结点;
|
||||
- 第三步: 移动指针,删除结点元素;
|
||||
|
||||
|
||||
```C++
|
||||
|
||||
// 获取删除位置结点元素的前驱结点
|
||||
p=GetElem(L,i-1);
|
||||
|
||||
// 删除位置结点元素指针
|
||||
q=p->next;
|
||||
|
||||
// 修改指针,将删除位置结点元素前驱结点的指针域指向其后继结点
|
||||
p->next=q->next;
|
||||
|
||||
// 释放结点元素的内存控件
|
||||
free(q)
|
||||
|
||||
```
|
||||
|
||||
和插入算法一样,时间都消耗在查询前驱结点上,时间复杂度为:O(n)
|
||||
|
||||
|
||||
|
||||
|
||||
> 删除单链表L中给点结点元素*p,通常是按值查找获取到p结点的前驱元素,再执行删除操作,这样很明显会导致时间复杂度为:O(n),主要都消耗在`按值查找`上
|
||||
|
||||
这里可以利用p结点的后继结点将p结点删除
|
||||
|
||||
- 第一步:申请结点q,使其只想p结点的后继结点;
|
||||
- 第二步:将p结点的数据域值换成其后继结点的数据域;【注意,交换没什么意义,最终p的后继结点会删除、释放】
|
||||
- 第三步:p的指针域指向q的指针域,q结点从链中“断开”
|
||||
- 第四步:释放q的内存空间
|
||||
|
||||
|
||||
```C++
|
||||
// 存放p的后继结点指针
|
||||
q=p->next;
|
||||
|
||||
// 结点p的后继结点元素赋值给结点p,避免后继结点的数据域丢失
|
||||
p->data=p->next->data;
|
||||
p->next=q->next;
|
||||
|
||||
// 此时q指向更换数据域后的p,即原来p的后继结点
|
||||
free(q)
|
||||
|
||||
```
|
||||
|
||||
相比按值查找前驱结点来删除给定的结点p,利用后继结点来删除的时间复杂度更小,为:O(1)
|
||||
|
||||
|
||||
|
||||
#### 计算表长
|
||||
|
||||
> 计算单链表中数据结点(不含头结点)的个数
|
||||
|
||||
|
||||
算法思路:从第一个结点开始顺序依次访问表中的每一个结点,为此需要设置一个`计数器变量`,每访问一个结点,计算器加1,直到访问到空结点为止。
|
||||
|
||||
算法时间复杂度:O(n)
|
||||
|
||||
|
||||
**单链表的长度是不包括头结点的,不带头结点和带头结点的单链表在求表长操作上会略有不同。**
|
||||
|
||||
不带头结点的单链表,当表为空时候,需要单独处理;
|
||||
|
||||
```C++
|
||||
// 不带头结点的单链表L为空,判定条件是L=NULL。
|
||||
if(L===NULL){
|
||||
// 链表为空,表长为0
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 带头结点的单链表L为空,判空条件:L->next=NULL;
|
||||
|
||||
if(L->next===NULL){
|
||||
// 链表为空,不包含头结点,表长为0
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
92
数据结构/线性表/4.线性表的链式表示【双链表】.md
Normal file
92
数据结构/线性表/4.线性表的链式表示【双链表】.md
Normal file
@@ -0,0 +1,92 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2021-03-07 21:59:05
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2021-03-07 21:59:13
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
## 双链表
|
||||
|
||||
|
||||
从单链表的结构上来看
|
||||
- 访问特定结点的前驱结点需要遍历整个单链表,移动指针,时间复杂度为:O(n)
|
||||
- 访问特定结点的后继结点只需要移动一次指针,时间复杂度为:O(1)
|
||||
|
||||
|
||||
双链表的引入,很好的解决单链表访问前驱结点时间消耗大的问题。
|
||||
|
||||
|
||||
双链表结点由三部分组成:
|
||||
|
||||
- `数据域` 存放数据信息
|
||||
- `prior指针域` 指向结点的前驱结点
|
||||
- `next指针域` 指向结点的后继结点
|
||||
|
||||
|
||||
```C++
|
||||
|
||||
// 双链表结点类型
|
||||
typedef struct DNode{
|
||||
ElemType data; // 结点的数据域
|
||||
struct DNode *prior; // 结点的前驱指针
|
||||
struct DNode *next; // 结点的后继指针
|
||||
}DNode, *DlinkList;
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 基本特点
|
||||
|
||||
- 双链表仅仅在单链表的结点中增加了一个指向结点前驱的`prior`指针;
|
||||
- `按值查找`、`按序号查找`在单链表和双链表上的操作是相同的。
|
||||
- 和单链表不同,`插入`、`删除`操作除了修改`next`指针域,双链表还需要修改`prior`指针域,确保不断`链`,时间复杂度都为:O(1)
|
||||
|
||||
|
||||
|
||||
|
||||
### 插入结点
|
||||
|
||||
> 在双链表中p所指的结点之后插入结点s
|
||||
|
||||
|
||||
```C++
|
||||
|
||||
// 第一步
|
||||
s->next=p->next;
|
||||
|
||||
// 第二步
|
||||
p->next->prior=s;
|
||||
|
||||
// 第三步
|
||||
s->prior=p;
|
||||
|
||||
// 第四步
|
||||
p->next=s
|
||||
|
||||
```
|
||||
|
||||
**第一步和第二步必须再第四步之前**,整体时间复杂度为:O(1)
|
||||
|
||||
### 删除结点
|
||||
|
||||
> 删除双链表中结点p的后继结点q
|
||||
|
||||
```C++
|
||||
|
||||
// 第一步
|
||||
p->next=q->next;
|
||||
|
||||
// 第二步
|
||||
q->next->prior=p;
|
||||
|
||||
// 第三步
|
||||
free(q);
|
||||
|
||||
```
|
||||
|
||||
**第一步和第二步顺序可换**,整体时间复杂度为:O(1)
|
||||
76
数据结构/线性表/5.线性表的链式表示【循环链表】.md
Normal file
76
数据结构/线性表/5.线性表的链式表示【循环链表】.md
Normal file
@@ -0,0 +1,76 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2021-03-07 21:59:27
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2021-03-07 21:59:27
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
## 循环链表
|
||||
|
||||
- 循环单链表
|
||||
- 循环双链表
|
||||
|
||||
|
||||
### 循环单链表
|
||||
|
||||
`循环单链表`是在单链表的基础上,将最后一个结点(尾结点)的指针由`NULL`改为指向`头结点`,形成`环`。【单链表----->循环单链表】
|
||||
|
||||
|
||||
```C++
|
||||
// 双链表结点类型
|
||||
typedef struct DNode{
|
||||
ElemType data; // 结点的数据域
|
||||
struct DNode *prior; // 结点的前驱指针
|
||||
struct DNode *next; // 结点的后继指针
|
||||
}DNode, *DlinkList;
|
||||
|
||||
```
|
||||
|
||||
#### 判空条件
|
||||
|
||||
**不是判断头结点的指针是否为空,而是需要判断是否等于头指针**,表为空时,头结点的next指针域其实是指向自己;
|
||||
|
||||
|
||||
|
||||
#### 特点
|
||||
|
||||
- 在循环单链表中,尾结点*p的next指针域指向链表L(即:头结点),形成了`闭环`,不存在指针域为`NULL`的结点。
|
||||
- **由于循环单链表是个`环`,在任何位置上的插入、删除操作都是等价的,不需要去判断是否是表尾**。当其中的结点的next指针指向自己,也就能判断表为空
|
||||
- 单链表只能从头结点(表头结点)开始往后顺序遍历整个表,循环单链表可以从表中任意位置开始遍历整个链表,结点是等价的;
|
||||
- **循环单链表可以抽象为时钟,形成的`环`是有顺序的;**
|
||||
- 频繁的`表头`和`表尾`操作,可以对循环单链表设置`尾指针`,而不设置`头指针`,明确尾指针r后,头指针即为:`r->next` ,减少头指针到尾指针间的遍历,时间复杂度:O(n)---->O(1)
|
||||
|
||||
|
||||
|
||||
|
||||
### 循环双链表
|
||||
|
||||
`循环双链表`是在双链表的基础上,将`尾结点`的`next`指针指向`头结点`,将`头结点`的`prior`指针指向`尾结点`。【双链表----->循环双链表】
|
||||
|
||||
|
||||
```C++
|
||||
// 双链表结点类型
|
||||
typedef struct DNode{
|
||||
ElemType data; // 结点的数据域
|
||||
struct DNode *prior; // 结点的前驱指针
|
||||
struct DNode *next; // 结点的后继指针
|
||||
}DNode, *DlinkList;
|
||||
|
||||
```
|
||||
|
||||
#### 判空条件
|
||||
|
||||
循环双链表为空时,头结点*p的prior指针和next指针都指向L,即同时满足:
|
||||
|
||||
- p->next=L
|
||||
- p->prior=L
|
||||
|
||||
|
||||
#### 基本特点
|
||||
|
||||
- 从双向链表中的任意一个结点开始,都可以很方便地访问它的`前驱结点`和`后继结点`。
|
||||
44
数据结构/线性表/6.线性表的链式表示【静态链表】.md
Normal file
44
数据结构/线性表/6.线性表的链式表示【静态链表】.md
Normal file
@@ -0,0 +1,44 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2021-03-07 21:59:47
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2021-03-07 21:59:47
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
## 静态链表
|
||||
|
||||
> 借助数组来描述线性表的链式存储结构,结点元素同样存在数据域`data`和指针域`next`
|
||||
|
||||
|
||||
注意: 和普通的链表的指针域不同的是,静态链表的指针是结点元素的相对地址(数组下标),也称为`游标`,建议结合高级语言中数组的概念来理解;
|
||||
|
||||
|
||||
**与顺序表一样,虽然静态链表属于链表,但是存储时需要预先分配一块连续的内存空间**
|
||||
|
||||
|
||||
静态链表结构类型:
|
||||
|
||||
|
||||
```C++
|
||||
|
||||
// 定义静态链表的最大长度
|
||||
# define MaxSize 50
|
||||
|
||||
typedef struct{
|
||||
ElemType data; // 存储数据元素,数据域
|
||||
int next; // 下个元素的相对地址,数组下标
|
||||
}SLinkList[MaxSize];
|
||||
|
||||
```
|
||||
|
||||
很显然,静态链表是通过`数组游标`来访问下一个结点元素,可以和`指针域`的相关概念结合理解;
|
||||
|
||||
|
||||
- 静态链表以`next=-1`作为结束的标志【尾结点】
|
||||
- 和动态链表相同,**插入、删除操作不需要移动元素,只需要修改指针**;
|
||||
- 总体来说,静态链表没有单链表使用方便,需要将整个链表存储在一块连续的内存空间中,内部的存储可以分散,通过指针构成`链`的关系
|
||||
72
数据结构/线性表/7.顺序表和链表的比较.md
Normal file
72
数据结构/线性表/7.顺序表和链表的比较.md
Normal file
@@ -0,0 +1,72 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2021-03-07 22:00:10
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2021-03-07 22:00:10
|
||||
-->
|
||||
|
||||
|
||||
|
||||
## 顺序表和链表的比较
|
||||
|
||||
|
||||
### 存取方式
|
||||
|
||||
- 顺序表支持顺序存取和随机存取;
|
||||
- 链表只能从表头顺序存取元素,支持顺序存取;
|
||||
|
||||
|
||||
### 逻辑结构与物理结构
|
||||
|
||||
|
||||
- 顺序存储时,逻辑上相邻的元素,对应的物理存储位置也相邻【一定性】。
|
||||
- 链式存储时,逻辑上相邻的元素,对应的物理存储位置不一定相邻【可以相邻,也可以不相邻】。
|
||||
- 链式存储的逻辑关系通过指针链接表示;
|
||||
|
||||
|
||||
|
||||
### 时间复杂度
|
||||
|
||||
#### 按值查找
|
||||
|
||||
- 顺序表无序的情况下,顺序表和链表的时间复杂度均为O(n)
|
||||
- 顺序表有序的情况下,顺序表的时间复杂度为O(log<sub>2</sub>n),链表的时间复杂度为O(n);
|
||||
|
||||
|
||||
**注意:O(log<sub>2</sub>n) < O(n)**
|
||||
|
||||
#### 按序号查找
|
||||
|
||||
- 顺序表支持随机访问,时间复杂度为O(1);
|
||||
- 顺序表不支持随机访问,时间复杂度为O(n);
|
||||
|
||||
#### 插入、删除
|
||||
|
||||
- 顺序表平均需要移动半个表长的元素;
|
||||
- 链表只需要修改相应结点的指针域,不需要移动元素;
|
||||
- 链表结点除了数据域,还有指针域,在存储空间上比顺序存储需要更大的存储空间,付出更大的存储代价,存储密度不够大
|
||||
|
||||
### 空间分配
|
||||
|
||||
#### 顺序存储
|
||||
|
||||
|
||||
##### 静态分配
|
||||
|
||||
- 需要预先分配足够大的存储空间;
|
||||
- 空间装满后不能扩充,存储新元素将出现`内存溢出`;
|
||||
- 存储空间过大,顺序表后部闲置空间过多,造成`内部碎片`
|
||||
- 存储空间过小,会造成`内存溢出`
|
||||
|
||||
##### 动态分配
|
||||
|
||||
- 动态分配能够扩充存储空间,但需要移动大量元素,操作效率降低
|
||||
- 内存中没有更大块的连续存储空间,将会导致空间分配失败;
|
||||
|
||||
|
||||
#### 链式存储
|
||||
|
||||
- 链式存储的结点空间只在需要的时候申请分配
|
||||
- 只要内存由空间就可以分配,操作灵活、高效
|
||||
45
数据结构/线性表/8.存储结构的选取.md
Normal file
45
数据结构/线性表/8.存储结构的选取.md
Normal file
@@ -0,0 +1,45 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2021-03-07 22:00:36
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2021-03-07 22:00:43
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
## 存储结构的选取
|
||||
|
||||
|
||||
|
||||
### 基于存储的考虑
|
||||
|
||||
- 对线性表的长度和存储规模难以估计时,不宜采用顺序表存储
|
||||
- 链表不用事先估计存储规模,但存储密度较低
|
||||
- 链式存储结构的存储密度小于1,不要求连续的存储空间
|
||||
|
||||
|
||||
|
||||
### 基于运算的考虑
|
||||
|
||||
- 顺序表支持随机存取,按序号查找顺序表的时间复杂度为O(1);
|
||||
- 链表不支持随机存取,按序号查找链表的时间复杂度为O(n);
|
||||
- 顺序表的插入、删除操作,平均需要移动表中一半的元素,当表的数据量较大时,这种情况需要重点考虑的。
|
||||
- 链表的插入、删除操作,也是需要找插入位置(前驱结点、后继结点),主要的操作还是比较操作,相对较好;
|
||||
|
||||
|
||||
|
||||
|
||||
### 基于环境的考虑
|
||||
|
||||
- 顺序表容易实现,任何高级语言中都有数组类型;
|
||||
- 链表操作是基于指针的,指针移动,相对复杂;
|
||||
|
||||
|
||||
|
||||
综上比较
|
||||
|
||||
- 通常比较稳定的线性表选择顺序存储;
|
||||
- 频繁进行插入、删除操作的线性表,应该选择链式存储,动态性较强
|
||||
33
数据结构/线性表/9.零碎知识补充.md
Normal file
33
数据结构/线性表/9.零碎知识补充.md
Normal file
@@ -0,0 +1,33 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Version: Beta1.0
|
||||
* @Author: 【B站&公众号】Rong姐姐好可爱
|
||||
* @Date: 2021-03-07 22:00:53
|
||||
* @LastEditors: 【B站&公众号】Rong姐姐好可爱
|
||||
* @LastEditTime: 2021-03-07 22:00:53
|
||||
-->
|
||||
|
||||
|
||||
|
||||
## 零碎知识补充
|
||||
|
||||
- 无论是链表的插入还是删除操作,必须保证不断链【重要】
|
||||
- 顺序存储结构可以随机存取也能顺序存取,链式结构只能进行顺序存取
|
||||
- 顺序存储方式同样适合图和树的存储,例如:满二叉树的顺序存储
|
||||
- 队列需要在表头删除元素,在表尾插入元素【先进先出】,采用带尾指针的循环单链表比较方便
|
||||
- 数组排序最少时间复杂度为O(nlog<sub>2</sub>n)【重要】
|
||||
- 静态链表中的指针称为`游标`,指示下一个元素在数组中的`下标`
|
||||
- 静态链表用数组表示,需要预先分配较大的连续空间,同时具有一般链表的特点(插入、删除元素不需要移动元素)
|
||||
|
||||
|
||||
### 单链表设置头结点
|
||||
|
||||
|
||||
目的
|
||||
|
||||
> 主要是方便运算。
|
||||
|
||||
好处
|
||||
|
||||
- 有头结点后,插入、删除数据元素的算法统一起来了,不需要判断是否在第一个元素之前插入或者删除第一个元素了。
|
||||
- 不论链表是否为空,头指针是指向头结点的`非空指针`,链表的头指针不变,因此空链表和非空链表的处理也就统一起来了。
|
||||
BIN
数据结构/线性表/线性表.png
Normal file
BIN
数据结构/线性表/线性表.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 80 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
Reference in New Issue
Block a user