mirror of
https://github.com/happyflyer/wangdao-data-structure.git
synced 2026-02-02 18:20:02 +08:00
完成单链表
This commit is contained in:
36
README.md
36
README.md
@@ -138,23 +138,7 @@ $$
|
||||
算法的时间复杂度(**常对幂指阶**):
|
||||
|
||||
$$
|
||||
O(1)
|
||||
\lt
|
||||
O(log_2n)
|
||||
\lt
|
||||
O(n)
|
||||
\lt
|
||||
O(nlog_2n)
|
||||
\lt
|
||||
O(n^2)
|
||||
\lt
|
||||
O(n^3)
|
||||
\lt
|
||||
O(2^n)
|
||||
\lt
|
||||
O(n!)
|
||||
\lt
|
||||
O(n^n)
|
||||
O(1) < O(log_2n) < O(n) < O(nlog_2n) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
|
||||
$$
|
||||
|
||||
- 结论一:顺序执行的代码只会影响常数项,可以忽略。
|
||||
@@ -229,23 +213,7 @@ $$
|
||||
常对幂指阶:
|
||||
|
||||
$$
|
||||
O(1)
|
||||
\lt
|
||||
O(log_2n)
|
||||
\lt
|
||||
O(n)
|
||||
\lt
|
||||
O(nlog_2n)
|
||||
\lt
|
||||
O(n^2)
|
||||
\lt
|
||||
O(n^3)
|
||||
\lt
|
||||
O(2^n)
|
||||
\lt
|
||||
O(n!)
|
||||
\lt
|
||||
O(n^n)
|
||||
O(1) < O(log_2n) < O(n) < O(nlog_2n) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
|
||||
$$
|
||||
|
||||
## 3. [线性表](ch2/README.md)
|
||||
|
||||
@@ -56,7 +56,12 @@ Tips:
|
||||
|
||||
## 4. [顺序表](sequence/README.md)
|
||||
|
||||
## 5. 单链表
|
||||
- 优点:可随机存取,存储密度高。
|
||||
- 缺点:要求大片连续空间,改变容量不方便。
|
||||
|
||||
## 5. [单链表](single-link/README.md)
|
||||
|
||||
- 缺点:无法逆向检索,有时候不太方便。
|
||||
|
||||
## 6. 双链表
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ C++ 可以使用 `new`,`delete` 关键字。
|
||||
## 3. 顺序表的特点
|
||||
|
||||
- **随机访问**:即可以在 $O(1)$ 时间内找到第 $i$ 个元素。
|
||||
- 存储密度高:每个节点只存储数据元素。
|
||||
- 存储密度高:每个结点只存储数据元素。
|
||||
- 扩展容量不方便:静态分配不能扩展容量,动态分配可以扩展但时间复杂度高。
|
||||
- 插入、删除元素操作不方便,需要移动大量元素。
|
||||
|
||||
|
||||
536
ch2/single-link/README.md
Normal file
536
ch2/single-link/README.md
Normal file
@@ -0,0 +1,536 @@
|
||||
# 单链表
|
||||
|
||||
## 1. typedef 关键词
|
||||
|
||||
`typedef` 关键字:数据类型重命名。
|
||||
|
||||
```cpp
|
||||
typedef <数据类型> <别名>
|
||||
```
|
||||
|
||||
```cpp
|
||||
int x = 1;
|
||||
int *p;
|
||||
```
|
||||
|
||||
```cpp
|
||||
typedef int zhengshu;
|
||||
typedef int *zhengshuzhizhen;
|
||||
zhengshu x = 1;
|
||||
zhengshuzhizhen p;
|
||||
```
|
||||
|
||||
## 2. 定义单链表
|
||||
|
||||
结点:
|
||||
|
||||
- 数据域
|
||||
- 指针域
|
||||
|
||||
```cpp
|
||||
struct LNode
|
||||
{
|
||||
ElemType data;
|
||||
struct LNode *next;
|
||||
};
|
||||
```
|
||||
|
||||
```cpp
|
||||
struct LNode *p = (struct LNode*) malloc(sizeof(struct LNode))
|
||||
```
|
||||
|
||||
```cpp
|
||||
typedef struct LNode LNode;
|
||||
LNode *p = (LNode *) malloc(sizeof LNode);
|
||||
```
|
||||
|
||||
高级写法:
|
||||
|
||||
```cpp
|
||||
typedef struct LNode
|
||||
{
|
||||
ElemType data;
|
||||
struct LNode *next;
|
||||
} LNode, *LinkList;
|
||||
```
|
||||
|
||||
```cpp
|
||||
struct LNode
|
||||
{
|
||||
ElemType data;
|
||||
struct LNode *next;
|
||||
};
|
||||
typedef struct LNode LNode;
|
||||
typedef struct LNode *LinkList;
|
||||
```
|
||||
|
||||
声明一个单链表时,只需要声明一个头指针 `L`,指向单链表的第一个结点。
|
||||
|
||||
```cpp
|
||||
LNode *L; // 强调这是一个结点
|
||||
```
|
||||
|
||||
```cpp
|
||||
LinkList L; // 强调这是一个单链表
|
||||
```
|
||||
|
||||
## 3. 不带头结点的单链表
|
||||
|
||||
```cpp
|
||||
// 初始化一个空的单链表,不带头结点
|
||||
bool InitList(LinkList &L)
|
||||
{
|
||||
L = NULL;
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
```cpp
|
||||
// 判断单链表是否为空
|
||||
bool Empty(LinkList L)
|
||||
{
|
||||
return L == NULL;
|
||||
}
|
||||
```
|
||||
|
||||
写代码更麻烦:
|
||||
|
||||
- 对第一个数据结点和后续数据结点的处理需要用不同的代码逻辑。
|
||||
- 对空表和非空表的处理需要用不同的代码逻辑。
|
||||
|
||||
## 4. 带头结点的单链表
|
||||
|
||||
头结点:
|
||||
|
||||
- `data` 没有数据
|
||||
- `next` 指向第一个结点,空表时为 `NULL`
|
||||
|
||||
```cpp
|
||||
// 初始化一个空的单链表,带头结点,头结点不存储数据
|
||||
bool InitList(LinkList &L)
|
||||
{
|
||||
// 分配一个头结点
|
||||
L = (LNode *)malloc(sizeof(LNode));
|
||||
if (L == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
L->next = NULL;
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
```cpp
|
||||
// 判断单链表是否为空
|
||||
bool Empty(LinkList L)
|
||||
{
|
||||
return L->next == NULL;
|
||||
}
|
||||
```
|
||||
|
||||
写代码更方便。
|
||||
|
||||
## 5. 按位序插入
|
||||
|
||||
### 5.1. 带头结点
|
||||
|
||||
`ListInsert(&L,i,e)` 步骤:
|
||||
|
||||
1. 找到第 $i-1$ 个结点 `p`
|
||||
2. 申请一个结点 `s`,存入数据元素 `e`
|
||||
3. 修改 `s` 的后继结点为 `p` 的后继结点
|
||||
4. 修改 `p` 的后继结点为 `s`
|
||||
|
||||
当插入位置 $i=1$ 时,可以把头结点看作为第 $0$ 个结点,以上逻辑同上适用。
|
||||
|
||||
```cpp
|
||||
// 在第 i 个位置插入元素 e,带头结点
|
||||
bool ListInsert(LinkList &L, int i, int e)
|
||||
{
|
||||
// i 的值必须是合法的位序
|
||||
if (i < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LNode *p;
|
||||
int j = 0; // 当前 p 指向的是第几个结点
|
||||
p = L; // 指向第 0 个结点
|
||||
while (p != NULL && j < i - 1) // 循环找到第 i-1 个结点
|
||||
{
|
||||
p = p->next;
|
||||
j++;
|
||||
}
|
||||
if (p == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LNode *s = (LNode *)malloc(sizeof(LNode));
|
||||
s->data = e;
|
||||
s->next = p->next;
|
||||
p->next = s;
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
- 最好时间复杂度:$O(1)$
|
||||
- 最坏时间复杂度:$O(n)$
|
||||
- 平均时间复杂度:$O(n)$
|
||||
|
||||
### 5.2. 不带头结点
|
||||
|
||||
插入位序 $i=1$ 时的步骤:
|
||||
|
||||
1. 申请一个结点 `s`,存入数据元素 `e`
|
||||
2. 修改 `s` 的后继结点为 `L` 指向的结点
|
||||
3. 修改 `L` 指向结点 `s`
|
||||
|
||||
插入位序 $i>1$ 时的步骤:
|
||||
|
||||
```cpp
|
||||
// 在第 i 个位置插入元素 e,不带头结点
|
||||
bool ListInsert(LinkList &L, int i, int e)
|
||||
{
|
||||
// i 的值必须是合法的位序
|
||||
if (i < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 插入第 1 个结点的操作与其他结点的操作不同
|
||||
if (i == 1)
|
||||
{
|
||||
LNode *s = (LNode *)malloc(sizeof(LNode));
|
||||
s->data = e;
|
||||
s->next = L;
|
||||
L = s;
|
||||
return true;
|
||||
}
|
||||
LNode *p;
|
||||
int j = 1; // 当前 p 指向的是第几个结点
|
||||
p = L; // 指向第 0 个结点
|
||||
while (p != NULL && j < i - 1) // 循环找到第 i-1 个结点
|
||||
{
|
||||
p = p->next;
|
||||
j++;
|
||||
}
|
||||
if (p == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LNode *s = (LNode *)malloc(sizeof(LNode));
|
||||
s->data = e;
|
||||
s->next = p->next;
|
||||
p->next = s;
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 指定结点的后插操作
|
||||
|
||||
1. 申请一个结点 `s`,存入数据元素 `e`
|
||||
2. 修改 `s` 的后继结点为 `p` 的后继结点
|
||||
3. 修改 `p` 的后继结点为 `s`
|
||||
|
||||
```cpp
|
||||
// 后插操作:在结点 p 后插入元素 e
|
||||
bool InsertNextNode(LNode *p, int e)
|
||||
{
|
||||
if (p == NUll)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LNode *s = (LNode *)malloc(sizeof(LNode));
|
||||
if (s == NULL) // 内存分配失败,可写可不写
|
||||
{
|
||||
return false;
|
||||
}
|
||||
s->data = e;
|
||||
s->next = p->next;
|
||||
p->next = s;
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
时间复杂度:$O(1)$
|
||||
|
||||
## 7. 指定结点的前插操作
|
||||
|
||||
思路一:
|
||||
|
||||
```cpp
|
||||
bool InsertPriorNode(LinkList L, LNode *p, int e);
|
||||
```
|
||||
|
||||
- 需要传入头结点
|
||||
- 时间复杂度:$O(n)$
|
||||
|
||||
**思路二**:
|
||||
|
||||
1. 在结点 `p` 后插入一个结点 `s`,`s` 的数据元素为 `e`
|
||||
2. 修改 `s` 的数据元素为 `p` 的数据元素
|
||||
3. 修改 `p` 的数据元素为 `e`
|
||||
|
||||
- 不需要传入头结点
|
||||
- 时间复杂度:$O(1)$
|
||||
|
||||
```cpp
|
||||
// 前插操作:在结点 p 前插入元素 e
|
||||
bool InsertPriorNode(LNode *p, int e)
|
||||
{
|
||||
if (p == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LNode *s = (LNode *)malloc(sizeof(LNode));
|
||||
s->next = p->next;
|
||||
p->next = s;
|
||||
s->data = p->data;
|
||||
p->data = e;
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
```cpp
|
||||
// 前插操作:在结点 p 前插入结点 s
|
||||
bool InsertPriorNode(LNode *p, LNode *s)
|
||||
{
|
||||
if (p == NULL || s == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
s->next = p->next;
|
||||
p->next = s;
|
||||
int temp = s->data;
|
||||
s->data = p->data;
|
||||
p->data = temp;
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
## 8. 按位序删除(带头结点)
|
||||
|
||||
`ListDelete(&L,i,&e)` 步骤:
|
||||
|
||||
1. 找到第 $i-1$ 个结点 `p` 和被删除结点 `q`
|
||||
2. 修改 `e` 的值为 `q` 的数据元素值
|
||||
3. 修改 `p` 的后继结点为 `q` 的后继结点
|
||||
4. 释放 `q`
|
||||
|
||||
```cpp
|
||||
// 按位序删除结点
|
||||
bool ListDelete(LinkList &l, int i, int &e)
|
||||
{
|
||||
// i 的值必须是合法的位序
|
||||
if (i < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LNode *p;
|
||||
int j = 0; // 当前 p 指向的是第几个结点
|
||||
p = L; // 指向第 0 个结点
|
||||
while (p != NULL && j < i - 1) // 循环找到第 i-1 个结点
|
||||
{
|
||||
p = p->next;
|
||||
j++;
|
||||
}
|
||||
if (p == NULL) // i 值不合法
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (p->next == NULL) // 第 i-1 个结点后已无其他结点
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LNode *q = p->next;
|
||||
e = q->data;
|
||||
p->next = q->next;
|
||||
free(q);
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
- 最好时间复杂度:$O(1)$
|
||||
- 最坏时间复杂度:$O(n)$
|
||||
- 平均时间复杂度:$O(n)$
|
||||
|
||||
## 9. 指定结点删除
|
||||
|
||||
类似于前插操作,`DeleteNode(LNode *p)` 的步骤:
|
||||
|
||||
1. 声明一个指针 `q` 指向结点 `p` 的后继结点
|
||||
2. 修改 `p` 的数据元素为 `q` 的数据元素
|
||||
3. 修改 `p` 的后继结点为 `q` 的后继结点
|
||||
4. 释放 `q`
|
||||
|
||||
> 但是如果结点 p 是表尾结点,此方法将不适用,因为找不到 p 的后继结点。只能从表头开始查找并删除。
|
||||
|
||||
## 10. 单链表的局限性
|
||||
|
||||
无法逆向检索,有时候不太方便。
|
||||
|
||||
- 带头结点和不带头结点的区别
|
||||
- “封装”的好处
|
||||
|
||||
## 11. 按位查找(带头结点)
|
||||
|
||||
```cpp
|
||||
// 按位查找:返回第 i 个元素,带头结点,头结点看作是第 0 个结点
|
||||
LNode *GetElem(LinkList L, int i)
|
||||
{
|
||||
if (i < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
LNode *p;
|
||||
int j = 0; // 当前 p 指向的是第几个结点
|
||||
p = L; // 指向第 0 个结点
|
||||
while (p != NULL && j < i) // 循环找到第 i-1 个结点
|
||||
{
|
||||
p = p->next;
|
||||
j++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
```
|
||||
|
||||
- 时间复杂度:$O(n)$
|
||||
|
||||
## 12. 按值查找(带头结点)
|
||||
|
||||
```cpp
|
||||
// 按值查找,找到数据域 == e 的结点
|
||||
int LocateElem(LinkList L, int e)
|
||||
{
|
||||
LNode *p = L->next;
|
||||
// 从第 1 个结点开始查找数据域为 e 的结点
|
||||
while (p != NULL && p->data != e)
|
||||
{
|
||||
p = p->next;
|
||||
}
|
||||
// 找到后返回该节点指针,否则返回 NULL
|
||||
return p;
|
||||
}
|
||||
```
|
||||
|
||||
- 时间复杂度:$O(n)$
|
||||
|
||||
## 13. 求表的长度
|
||||
|
||||
```cpp
|
||||
// 求表的长度
|
||||
int Length(LinkList L)
|
||||
{
|
||||
int len = 0;
|
||||
LNode *p = L;
|
||||
while (p->next != NULL)
|
||||
{
|
||||
p = p->next;
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
```
|
||||
|
||||
- 时间复杂度:$O(n)$
|
||||
|
||||
## 14. 单链表的建立
|
||||
|
||||
单链表的建立步骤:
|
||||
|
||||
1. 初始化一个单链表
|
||||
2. 每次取一个数据元素,插入到表尾/表头
|
||||
|
||||
### 14.1. 尾插法
|
||||
|
||||
思路一:
|
||||
|
||||
1. 初始化单链表
|
||||
2. 设置变量 `length` 记录链表长度
|
||||
3. `while` 循环:
|
||||
1. 每次取一个数据元素 `e`
|
||||
2. `ListInsert(L, length+1, e)` 插到尾部
|
||||
3. `length++`
|
||||
|
||||
- 时间复杂度:$O(n^2)$
|
||||
|
||||
**思路二**:
|
||||
|
||||
1. 初始化单链表
|
||||
2. 设置指针 `r` 指向表尾元素
|
||||
3. `while` 循环:
|
||||
1. 在 `r` 之后插入结点 `s`
|
||||
2. 修改 `r` 指向 `s`
|
||||
|
||||
```cpp
|
||||
// 尾插法建立单链表
|
||||
LinkList List_TailInsert(LinkList &L)
|
||||
{
|
||||
int x;
|
||||
L = (LNode *)malloc(sizeof(LNode));
|
||||
LNode *s, *r = L; // r 为表尾指针
|
||||
scanf("%d", &x);
|
||||
while (x != 9999)
|
||||
{
|
||||
s = (LNode *)malloc(sizeof(LNode));
|
||||
s->data = x;
|
||||
r->next = s;
|
||||
r = s; // r 指向新的表尾结点
|
||||
scanf("%d", &x);
|
||||
}
|
||||
r->next = NULL;
|
||||
return L;
|
||||
}
|
||||
```
|
||||
|
||||
- 时间复杂度:$O(n)$
|
||||
|
||||
### 14.2. 头插法
|
||||
|
||||
1. 初始化单链表
|
||||
2. `while` 循环:
|
||||
1. 每次取一个数据元素 `e`
|
||||
2. `InsertNextNode(L, e)`
|
||||
|
||||
```cpp
|
||||
// 头插法建立单链表
|
||||
LinkList List_HeadInsert(LinkList &L)
|
||||
{
|
||||
int x;
|
||||
L = (LNode *)malloc(sizeof(LNode));
|
||||
L->next = NULL; // 一定要初始化头结点的 next
|
||||
LNode *s;
|
||||
scanf("%d", &x);
|
||||
while (x != 9999)
|
||||
{
|
||||
s = (LNode *)malloc(sizeof(LNode));
|
||||
s->data = x;
|
||||
s->next = L->next;
|
||||
L->next = s;
|
||||
scanf("%d", &x);
|
||||
}
|
||||
return L;
|
||||
}
|
||||
```
|
||||
|
||||
- 时间复杂度:$O(n)$
|
||||
|
||||
> 养成好习惯:只要是初始化单链表,就先把头指针指向 NULL。
|
||||
|
||||
头插法的重要应用:链表的逆置。
|
||||
|
||||
```cpp
|
||||
LinkList List_Reverse(LinkList &L)
|
||||
{
|
||||
LinkList L2 = (LNode *)malloc(sizeof(LNode));
|
||||
L2->next = NULL;
|
||||
LNode *s = L->next;
|
||||
while (s != NULL)
|
||||
{
|
||||
LNode *s2 = (LNode *)malloc(sizeof(LNode));
|
||||
s2->data = s->data;
|
||||
s2->next = L2->next;
|
||||
L2->next = s2;
|
||||
s = s->next;
|
||||
}
|
||||
return L2;
|
||||
}
|
||||
```
|
||||
98
ch2/single-link/tail_and_head.cpp
Normal file
98
ch2/single-link/tail_and_head.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
typedef struct LNode
|
||||
{
|
||||
int data;
|
||||
struct LNode *next;
|
||||
} LNode, *LinkList;
|
||||
|
||||
bool InitList(LinkList &L)
|
||||
{
|
||||
L = (LNode *)malloc(sizeof(LNode));
|
||||
if (L == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
L->next = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 尾插法建立单链表
|
||||
LinkList List_TailInsert(LinkList &L)
|
||||
{
|
||||
int x;
|
||||
L = (LNode *)malloc(sizeof(LNode));
|
||||
LNode *s, *r = L; // r 为表尾指针
|
||||
scanf("%d", &x);
|
||||
while (x != 9999)
|
||||
{
|
||||
s = (LNode *)malloc(sizeof(LNode));
|
||||
s->data = x;
|
||||
r->next = s;
|
||||
r = s; // r 指向新的表尾结点
|
||||
scanf("%d", &x);
|
||||
}
|
||||
r->next = NULL;
|
||||
return L;
|
||||
}
|
||||
|
||||
// 头插法建立单链表
|
||||
LinkList List_HeadInsert(LinkList &L)
|
||||
{
|
||||
int x;
|
||||
L = (LNode *)malloc(sizeof(LNode));
|
||||
L->next = NULL; // 一定要初始化头结点的 next
|
||||
LNode *s;
|
||||
scanf("%d", &x);
|
||||
while (x != 9999)
|
||||
{
|
||||
s = (LNode *)malloc(sizeof(LNode));
|
||||
s->data = x;
|
||||
s->next = L->next;
|
||||
L->next = s;
|
||||
scanf("%d", &x);
|
||||
}
|
||||
return L;
|
||||
}
|
||||
|
||||
// 头插法实现单链表的逆置
|
||||
LinkList List_Reverse(LinkList &L)
|
||||
{
|
||||
LinkList L2 = (LNode *)malloc(sizeof(LNode));
|
||||
L2->next = NULL;
|
||||
LNode *s = L->next;
|
||||
while (s != NULL)
|
||||
{
|
||||
LNode *s2 = (LNode *)malloc(sizeof(LNode));
|
||||
s2->data = s->data;
|
||||
s2->next = L2->next;
|
||||
L2->next = s2;
|
||||
s = s->next;
|
||||
}
|
||||
return L2;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
LinkList L;
|
||||
List_TailInsert(L);
|
||||
// List_HeadInsert(L);
|
||||
// 打印操作
|
||||
LNode *p;
|
||||
p = L->next;
|
||||
while (p != NULL)
|
||||
{
|
||||
printf("%d\n", p->data);
|
||||
p = p->next;
|
||||
}
|
||||
// 测试单链表逆置
|
||||
LinkList L2 = List_Reverse(L);
|
||||
// 打印操作
|
||||
p = L2->next;
|
||||
while (p != NULL)
|
||||
{
|
||||
printf("%d\n", p->data);
|
||||
p = p->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
224
ch2/single-link/with.cpp
Normal file
224
ch2/single-link/with.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
typedef struct LNode
|
||||
{
|
||||
int data;
|
||||
struct LNode *next;
|
||||
} LNode, *LinkList;
|
||||
|
||||
// 初始化一个空的单链表,带头结点,头结点不存储数据
|
||||
bool InitList(LinkList &L)
|
||||
{
|
||||
// 分配一个头结点
|
||||
L = (LNode *)malloc(sizeof(LNode));
|
||||
if (L == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
L->next = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 判断单链表是否为空
|
||||
bool Empty(LinkList L)
|
||||
{
|
||||
return L->next == NULL;
|
||||
}
|
||||
|
||||
bool InsertNextNode(LNode *p, int e);
|
||||
LNode *GetElem(LinkList L, int i);
|
||||
|
||||
// 在第 i 个位置插入元素 e,带头结点
|
||||
bool ListInsert(LinkList &L, int i, int e)
|
||||
{
|
||||
// i 的值必须是合法的位序
|
||||
if (i < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// LNode *p;
|
||||
// int j = 0; // 当前 p 指向的是第几个结点
|
||||
// p = L; // 指向第 0 个结点
|
||||
// while (p != NULL && j < i - 1) // 循环找到第 i-1 个结点
|
||||
// {
|
||||
// p = p->next;
|
||||
// j++;
|
||||
// }
|
||||
LNode *p = GetElem(L, i - 1);
|
||||
// if (p == NULL)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// LNode *s = (LNode *)malloc(sizeof(LNode));
|
||||
// s->data = e;
|
||||
// s->next = p->next;
|
||||
// p->next = s;
|
||||
// return true;
|
||||
return InsertNextNode(p, e);
|
||||
}
|
||||
|
||||
// 后插操作:在结点 p 后插入元素 e
|
||||
bool InsertNextNode(LNode *p, int e)
|
||||
{
|
||||
if (p == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LNode *s = (LNode *)malloc(sizeof(LNode));
|
||||
if (s == NULL) // 内存分配失败,可写可不写
|
||||
{
|
||||
return false;
|
||||
}
|
||||
s->data = e;
|
||||
s->next = p->next;
|
||||
p->next = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 前插操作:在结点 p 前插入元素 e
|
||||
bool InsertPriorNode(LNode *p, int e)
|
||||
{
|
||||
if (p == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LNode *s = (LNode *)malloc(sizeof(LNode));
|
||||
s->next = p->next;
|
||||
p->next = s;
|
||||
s->data = p->data;
|
||||
p->data = e;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 前插操作:在结点 p 前插入结点 s
|
||||
bool InsertPriorNode(LNode *p, LNode *s)
|
||||
{
|
||||
if (p == NULL || s == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
s->next = p->next;
|
||||
p->next = s;
|
||||
int temp = s->data;
|
||||
s->data = p->data;
|
||||
p->data = temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 按位序删除结点,带头结点
|
||||
bool ListDelete(LinkList &L, int i, int &e)
|
||||
{
|
||||
// i 的值必须是合法的位序
|
||||
if (i < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// LNode *p;
|
||||
// int j = 0; // 当前 p 指向的是第几个结点
|
||||
// p = L; // 指向第 0 个结点
|
||||
// while (p != NULL && j < i - 1) // 循环找到第 i-1 个结点
|
||||
// {
|
||||
// p = p->next;
|
||||
// j++;
|
||||
// }
|
||||
LNode *p = GetElem(L, i - 1);
|
||||
if (p == NULL) // i 值不合法
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (p->next == NULL) // 第 i-1 个结点后已无其他结点
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LNode *q = p->next;
|
||||
e = q->data;
|
||||
p->next = q->next;
|
||||
free(q);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 按位查找:返回第 i 个元素,带头结点,头结点看作是第 0 个结点
|
||||
LNode *GetElem(LinkList L, int i)
|
||||
{
|
||||
if (i < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
LNode *p;
|
||||
int j = 0; // 当前 p 指向的是第几个结点
|
||||
p = L; // 指向第 0 个结点
|
||||
while (p != NULL && j < i) // 循环找到第 i-1 个结点
|
||||
{
|
||||
p = p->next;
|
||||
j++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// 按值查找,找到数据域 == e 的结点
|
||||
LNode *LocateElem(LinkList L, int e)
|
||||
{
|
||||
LNode *p = L->next;
|
||||
// 从第 1 个结点开始查找数据域为 e 的结点
|
||||
while (p != NULL && p->data != e)
|
||||
{
|
||||
p = p->next;
|
||||
}
|
||||
// 找到后返回该节点指针,否则返回 NULL
|
||||
return p;
|
||||
}
|
||||
|
||||
// 求表的长度
|
||||
int Length(LinkList L)
|
||||
{
|
||||
int len = 0;
|
||||
LNode *p = L;
|
||||
while (p->next != NULL)
|
||||
{
|
||||
p = p->next;
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
LinkList L;
|
||||
InitList(L);
|
||||
ListInsert(L, 1, 1);
|
||||
ListInsert(L, 2, 2);
|
||||
ListInsert(L, 3, 3);
|
||||
ListInsert(L, 4, 4);
|
||||
ListInsert(L, 5, 5);
|
||||
ListInsert(L, 3, 100);
|
||||
// 打印操作
|
||||
LNode *p;
|
||||
p = L->next;
|
||||
while (p != NULL)
|
||||
{
|
||||
printf("%d\n", p->data);
|
||||
p = p->next;
|
||||
}
|
||||
// 测试删除操作
|
||||
int e = -1;
|
||||
if (ListDelete(L, 5, e))
|
||||
{
|
||||
printf("已删除的元素值为=%d\n", e);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("位序不合法,删除失败\n");
|
||||
}
|
||||
// 打印操作
|
||||
p = L->next;
|
||||
while (p != NULL)
|
||||
{
|
||||
printf("%d\n", p->data);
|
||||
p = p->next;
|
||||
}
|
||||
// 测试按位查找
|
||||
printf("位序为4的结点为=%s\n", GetElem(L, 4));
|
||||
// 测试按值查找
|
||||
printf("元素值为4的结点=%s\n", LocateElem(L, 4));
|
||||
return 0;
|
||||
}
|
||||
127
ch2/single-link/without.cpp
Normal file
127
ch2/single-link/without.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
typedef struct LNode
|
||||
{
|
||||
int data;
|
||||
struct LNode *next;
|
||||
} LNode, *LinkList;
|
||||
|
||||
// 初始化一个空的单链表,不带头结点
|
||||
bool InitList(LinkList &L)
|
||||
{
|
||||
L = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 判断单链表是否为空
|
||||
bool Empty(LinkList L)
|
||||
{
|
||||
return L == NULL;
|
||||
}
|
||||
|
||||
bool InsertNextNode(LNode *p, int e);
|
||||
|
||||
// 在第 i 个位置插入元素 e,不带头结点
|
||||
bool ListInsert(LinkList &L, int i, int e)
|
||||
{
|
||||
// i 的值必须是合法的位序
|
||||
if (i < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 插入第 1 个结点的操作与其他结点的操作不同
|
||||
if (i == 1)
|
||||
{
|
||||
LNode *s = (LNode *)malloc(sizeof(LNode));
|
||||
s->data = e;
|
||||
s->next = L;
|
||||
L = s;
|
||||
return true;
|
||||
}
|
||||
LNode *p;
|
||||
int j = 1; // 当前 p 指向的是第几个结点
|
||||
p = L; // 指向第 0 个结点
|
||||
while (p != NULL && j < i - 1) // 循环找到第 i-1 个结点
|
||||
{
|
||||
p = p->next;
|
||||
j++;
|
||||
}
|
||||
// if (p == NULL)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// LNode *s = (LNode *)malloc(sizeof(LNode));
|
||||
// s->data = e;
|
||||
// s->next = p->next;
|
||||
// p->next = s;
|
||||
// return true;
|
||||
InsertNextNode(p, e);
|
||||
}
|
||||
|
||||
// 后插操作:在结点 p 后插入元素 e
|
||||
bool InsertNextNode(LNode *p, int e)
|
||||
{
|
||||
if (p == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LNode *s = (LNode *)malloc(sizeof(LNode));
|
||||
if (s == NULL) // 内存分配失败,可写可不写
|
||||
{
|
||||
return false;
|
||||
}
|
||||
s->data = e;
|
||||
s->next = p->next;
|
||||
p->next = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 前插操作:在结点 p 前插入元素 e
|
||||
bool InsertPriorNode(LNode *p, int e)
|
||||
{
|
||||
if (p == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LNode *s = (LNode *)malloc(sizeof(LNode));
|
||||
s->next = p->next;
|
||||
p->next = s;
|
||||
s->data = p->data;
|
||||
p->data = e;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 前插操作:在结点 p 前插入结点 s
|
||||
bool InsertPriorNode(LNode *p, LNode *s)
|
||||
{
|
||||
if (p == NULL || s == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
s->next = p->next;
|
||||
p->next = s;
|
||||
int temp = s->data;
|
||||
s->data = p->data;
|
||||
p->data = temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
LinkList L;
|
||||
InitList(L);
|
||||
ListInsert(L, 1, 1);
|
||||
ListInsert(L, 2, 2);
|
||||
ListInsert(L, 3, 3);
|
||||
ListInsert(L, 4, 4);
|
||||
ListInsert(L, 5, 5);
|
||||
ListInsert(L, 3, 100);
|
||||
// 打印操作
|
||||
LNode *p = L;
|
||||
while (p != NULL)
|
||||
{
|
||||
printf("%d\n", p->data);
|
||||
p = p->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user