线性表-单链表
顺序表
上一篇文章
链表介绍:
-
线性表链式存储结构的特点是:用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。
-
为了表示每个数据元素 Ai 与其直接后继数据元素 Ai+1之间的逻辑关系,对数据元素Ai来说,除了其本身的信息之外,还需要存储一个指示其直接后继的信息(直接后继的存储位置)。这两部分信息组成数据元素Ai的存储映像,称为节点(node)。
-
结点包括两个域:其中存储数据元素信息的称为数据域;存储直接后继存储位置有域称为指针域。指针域中存储的信息称作指针或链。
-
n个结点 [ A(1 ≤ i ≤ n)的存储映像 ] 链接成一个链表,即为线性表(A1,A2,...,An)。
链表存储结构
c
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType; // 定义元素类型
typedef struct node // 定义节点类型
{
ElemType data;
struct node *next;
} Node;
单链表
初始化
注意:
头节点:最开始的头节点
第一个节点:头节点指向的节点叫做第一个节点
对照这个图,完成单链表的初始化
c
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType; // 定义元素类型
typedef struct node // 定义节点类型
{
ElemType data;
struct node *next;
} Node;
/* 初始化一个单链表-造一个头节点 */
Node *InitList()
{
Node *head = (Node *)malloc(sizeof(Node)); // 为头节点分配内存
head->data = 0; // 头节点的数据域为0
head->next = NULL; // 头节点的指针域为空
return head; // 返回头节点
}
int main()
{
Node *list = InitList(); // 初始化一个单链表
printf("Hello World!\n");
return 0;
}
插入数据
链表的插入数据有两种方式:头插法和尾插法
头插法
每次都在头节点后面插入数据
第一次插入数据
第二次插入数据
可以看到:每次都在头节点后面插入数据
讲解视频,演示动画:【《数据结构(C 语言描述)》也许是全站最良心最通俗易懂最好看的数据结构课(最迟每周五更新~~)】 【精准空降到 1:31:50】 https://www.bilibili.com/video/BV1tNpbekEht/?p=3\&share_source=copy_web\&vd_source=8af85e60c2df9af1f0fd23935753a933\&t=5510
头插法的步骤:
- 创建一个新的节点
- 在新节点的数据域存入数据e
- 新节点的指针域指向头节点的下一个节点(把L的NULL复制给新节点)
- 头节点的指针域指向新节点
c
/*单链表 - 头插法*/
int InsertHead(Node *L, ElemType e)
{
Node *p = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
p->data = e; // 在新节点的数据域存入数据e
p->next = L->next; // 新节点的指针域指向头节点的下一个节点(把L的NULL复制给新节点)
L->next = p; // 头节点的指针域指向新节点
return 1; // 返回1表示成功
}
如果后面已经有了数据,那么头插法的演示效果如下,先让新节点指向第一个节点,再让头节点指向新节点(这样就能防止后面的数据丢失)
- 对比:顺序表的插入是在数组里插入,每个元素都往后移动,链表的插入不是这样的,只改变节点之间的指向关系即可实现!!!
单链表:单个方向的链表
双向链表:知道下一个,也知道上一个
循环链表:构成一个环的链表
尾插法
找到尾NULL,尾节点地址,根据这个地址再插入数据。
c
/* 单链表 - 尾插法 */
// 获取尾节点地址
Node *GetTail(Node *List)
{
Node *p = List; // 从头节点开始遍历
while (p->next != NULL) // 遍历到链表末尾
{
p = p->next; // 移动到下一个节点
}
return p; // 返回尾节点
}
// 尾插法插入数据
Node *InsertTail(Node *tail, ElemType e)
{
Node *p = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
p->data = e; // 在新节点的数据域存入数据e
tail->next = p; // 尾节点的指针域指向新节点
p->next = NULL; // 新节点的指针域为空
return p; // 返回新的尾节点
}
完整的尾插法代码
c
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType; // 定义元素类型
typedef struct node // 定义节点类型
{
ElemType data;
struct node *next;
} Node;
/* 初始化一个单链表-造一个头节点 */
Node *InitList()
{
Node *head = (Node *)malloc(sizeof(Node)); // 为头节点分配内存
head->data = 0; // 头节点的数据域为0
head->next = NULL; // 头节点的指针域为空
return head; // 返回头节点
}
/*单链表 - 头插法*/
int InsertHead(Node *L, ElemType e)
{
Node *p = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
p->data = e; // 在新节点的数据域存入数据e
p->next = L->next; // 新节点的指针域指向头节点的下一个节点(把L的NULL复制给新节点)
L->next = p; // 头节点的指针域指向新节点
return 1; // 返回1表示成功
}
/* 单链表 - 遍历 */
void TraverseList(Node *L)
{
Node *p = L->next; // 从头节点的下一个节点开始遍历
while (p != NULL) // 遍历到链表末尾
{
printf("%d ", p->data); // 输出节点的数据域
p = p->next; // 移动到下一个节点
}
printf("\n"); // 换行
}
/* 单链表 - 尾插法 */
// 获取尾节点地址
Node *GetTail(Node *List)
{
Node *p = List; // 从头节点开始遍历
while (p->next != NULL) // 遍历到链表末尾
{
p = p->next; // 移动到下一个节点
}
return p; // 返回尾节点
}
// 尾插法插入数据
Node *InsertTail(Node *tail, ElemType e)
{
Node *p = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
p->data = e; // 在新节点的数据域存入数据e
tail->next = p; // 尾节点的指针域指向新节点
p->next = NULL; // 新节点的指针域为空
return p; // 返回新的尾节点
}
int main()
{
Node *list = InitList(); // 初始化一个单链表
printf("头插法插入数据1 2 3\n");
InsertHead(list, 1); // 头插法插入数据1
InsertHead(list, 2); // 头插法插入数据2
InsertHead(list, 3); // 头插法插入数据3
printf("遍历-链表中的元素为:");
TraverseList(list); // 遍历单链表
printf("\n"); // 换行
printf("\n尾插法插入数据4 5 6\n");
Node *tail = GetTail(list); // 获取尾节点地址
tail = InsertTail(tail, 4); // 尾插法插入数据4
tail = InsertTail(tail, 5); // 尾插法插入数据5
tail = InsertTail(tail, 6); // 尾插法插入数据6
printf("遍历-链表中的元素为:");
TraverseList(list); // 遍历单链表
return 0;
}
输出结果如下:
c
头插法插入数据1 2 3
遍历-链表中的元素为:3 2 1
尾插法插入数据4 5 6
遍历-链表中的元素为:3 2 1 4 5 6
请按任意键继续. . .
可以看到尾插法的数据是正确的!
在指定位置插入数据
在70和80中间插入一个new
一定要注意这个顺序,不能颠倒
c
**
* @Description:单链表 - 在指定位置插入数据
* @param {Node} *L 单链表的头节点
* @param {int} pos 位置
* @param {ElemType} e 插入的数据
* @return {*}
*/
int InsertPosNode(Node *L, int pos, ElemType e)
{
// 用来保存插入位置的前驱节点
Node *p = L; // 从头节点开始遍历
int i = 0;
// 遍历链表-找到插入位置的前驱节点
while (i < pos - 1) // 遍历到插入位置的前驱节点
{
p = p->next; // 移动到下一个节点
i++;
if (p == NULL) // 判断是否到达链表末尾
{
printf("插入位置不合法\n");
return 0;
}
}
Node *newnode = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
newnode->data = e; // 在新节点的数据域存入数据e
newnode->next = p->next; // 新节点的指针域指向插入位置的前驱节点的下一个节点
p->next = newnode; // 插入位置的前驱节点的指针域指向新节点
return 1;
}
完整的演示代码:
c
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType; // 定义元素类型
typedef struct node // 定义节点类型
{
ElemType data;
struct node *next;
} Node;
/* 初始化一个单链表-造一个头节点 */
Node *InitList()
{
Node *head = (Node *)malloc(sizeof(Node)); // 为头节点分配内存
head->data = 0; // 头节点的数据域为0
head->next = NULL; // 头节点的指针域为空
return head; // 返回头节点
}
/*单链表 - 头插法*/
int InsertHead(Node *L, ElemType e)
{
Node *p = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
p->data = e; // 在新节点的数据域存入数据e
p->next = L->next; // 新节点的指针域指向头节点的下一个节点(把L的NULL复制给新节点)
L->next = p; // 头节点的指针域指向新节点
return 1; // 返回1表示成功
}
/* 单链表 - 遍历 */
void TraverseList(Node *L)
{
Node *p = L->next; // 从头节点的下一个节点开始遍历
while (p != NULL) // 遍历到链表末尾
{
printf("%d ", p->data); // 输出节点的数据域
p = p->next; // 移动到下一个节点
}
printf("\n"); // 换行
}
/* 单链表 - 尾插法 */
// 获取尾节点地址
Node *GetTail(Node *List)
{
Node *p = List; // 从头节点开始遍历
while (p->next != NULL) // 遍历到链表末尾
{
p = p->next; // 移动到下一个节点
}
return p; // 返回尾节点
}
/**
* @Description:单链表 - 尾插法插入数据
* @param {Node} *tail 尾节点
* @param {ElemType} e 插入的数据
* @return {*} 返回新的尾节点
*/
Node *InsertTail(Node *tail, ElemType e)
{
Node *p = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
p->data = e; // 在新节点的数据域存入数据e
tail->next = p; // 尾节点的指针域指向新节点
p->next = NULL; // 新节点的指针域为空
return p; // 返回新的尾节点
}
/**
* @Description:单链表 - 在指定位置插入数据
* @param {Node} *L 单链表的头节点
* @param {int} pos 位置
* @param {ElemType} e 插入的数据
* @return {*}
*/
int InsertPosNode(Node *L, int pos, ElemType e)
{
// 用来保存插入位置的前驱节点
Node *p = L; // 从头节点开始遍历
int i = 0;
// 遍历链表-找到插入位置的前驱节点
while (i < pos - 1) // 遍历到插入位置的前驱节点
{
p = p->next; // 移动到下一个节点
i++;
if (p == NULL) // 判断是否到达链表末尾
{
printf("插入位置不合法\n");
return 0;
}
}
Node *newnode = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
newnode->data = e; // 在新节点的数据域存入数据e
newnode->next = p->next; // 新节点的指针域指向插入位置的前驱节点的下一个节点
p->next = newnode; // 插入位置的前驱节点的指针域指向新节点
return 1;
}
int main()
{
Node *list = InitList(); // 初始化一个单链表
printf("头插法插入数据1 2 3\n");
InsertHead(list, 1); // 头插法插入数据1
InsertHead(list, 2); // 头插法插入数据2
InsertHead(list, 3); // 头插法插入数据3
printf("遍历-链表中的元素为:");
TraverseList(list); // 遍历单链表
printf("\n"); // 换行
printf("\n尾插法插入数据4 5 6\n");
Node *tail = GetTail(list); // 获取尾节点地址
tail = InsertTail(tail, 4); // 尾插法插入数据4
tail = InsertTail(tail, 5); // 尾插法插入数据5
tail = InsertTail(tail, 6); // 尾插法插入数据6
printf("遍历-链表中的元素为:");
TraverseList(list); // 遍历单链表
printf("\n在指定位置3插入数据7\n");
InsertPosNode(list, 3, 7); // 在指定位置3插入数据7
printf("遍历-链表中的元素为:");
TraverseList(list); // 遍历单链表
return 0;
}
输出结果
c
头插法插入数据1 2 3
遍历-链表中的元素为:3 2 1
尾插法插入数据4 5 6
遍历-链表中的元素为:3 2 1 4 5 6
在指定位置3插入数据7
遍历-链表中的元素为:3 2 7 1 4 5 6
请按任意键继续. . .
在指定位置3插入数据7,插入数据成功!
遍历链表
传入头节点的下一个节点,然后再while循环里遍历,直到指向NULL为止,输出链表的数据域内容即可
c
/* 单链表 - 遍历 */
void TraverseList(Node *L)
{
Node *p = L->next; // 从头节点的下一个节点开始遍历
while (p != NULL) // 遍历到链表末尾
{
printf("%d ", p->data); // 输出节点的数据域
p = p->next; // 移动到下一个节点
}
printf("\n"); // 换行
}
完整代码如下:
c
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType; // 定义元素类型
typedef struct node // 定义节点类型
{
ElemType data;
struct node *next;
} Node;
/* 初始化一个单链表-造一个头节点 */
Node *InitList()
{
Node *head = (Node *)malloc(sizeof(Node)); // 为头节点分配内存
head->data = 0; // 头节点的数据域为0
head->next = NULL; // 头节点的指针域为空
return head; // 返回头节点
}
/*单链表 - 头插法*/
int InsertHead(Node *L, ElemType e)
{
Node *p = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
p->data = e; // 在新节点的数据域存入数据e
p->next = L->next; // 新节点的指针域指向头节点的下一个节点(把L的NULL复制给新节点)
L->next = p; // 头节点的指针域指向新节点
return 1; // 返回1表示成功
}
/* 单链表 - 遍历 */
void TraverseList(Node *L)
{
Node *p = L->next; // 从头节点的下一个节点开始遍历
while (p != NULL) // 遍历到链表末尾
{
printf("%d ", p->data); // 输出节点的数据域
p = p->next; // 移动到下一个节点
}
printf("\n"); // 换行
}
int main()
{
Node *list = InitList(); // 初始化一个单链表
InsertHead(list, 1); // 头插法插入数据1
InsertHead(list, 2); // 头插法插入数据2
InsertHead(list, 3); // 头插法插入数据3
printf("遍历-链表中的元素为:");
TraverseList(list); // 遍历单链表
return 0;
}
输出:
cpp
遍历-链表中的元素为:3 2 1
请按任意键继续. . .
按照头插法,依次插入 1 2 3,遍历链表输出 3 2 1,这是正确的!
删除节点
找到被删除节点的前驱节点p
用指针q记录被删除的节点
通过改变p的后继节点实现删除
释放删除节点的空间
删除操作:
c
/**
* @Description:单链表 - 删除指定位置的节点
* @param {Node} *L 单链表的头节点
* @param {int} pos 位置
* @return {*} 返回1表示成功
*/
int DeletePosNode(Node *L, int pos)
{
// 用来保存删除位置的前驱节点
Node *p = L; // 从头节点开始遍历
int i = 0;
// 遍历链表-找到删除节点的前驱节点
while (i < pos - 1) // 遍历到删除位置的前驱节点
{
p = p->next; // 移动到下一个节点
i++;
if (p == NULL) // 判断是否到达链表末尾
{
printf("删除位置不合法\n");
return 0;
}
}
if (p->next == NULL) // 判断删除位置是否合法
{
printf("删除位置不合法\n");
return 0;
}
Node *q = p->next; // 保存要删除的节点的地址
p->next = q->next; // 删除节点的前驱节点的指针域 指向 删除节点的下一个节点
free(q); // 释放删除节点的内存
return 1; // 返回1表示成功
}
完整的删除节点代码:
c
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType; // 定义元素类型
typedef struct node // 定义节点类型
{
ElemType data;
struct node *next;
} Node;
/* 初始化一个单链表-造一个头节点 */
Node *InitList()
{
Node *head = (Node *)malloc(sizeof(Node)); // 为头节点分配内存
head->data = 0; // 头节点的数据域为0
head->next = NULL; // 头节点的指针域为空
return head; // 返回头节点
}
/*单链表 - 头插法*/
int InsertHead(Node *L, ElemType e)
{
Node *p = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
p->data = e; // 在新节点的数据域存入数据e
p->next = L->next; // 新节点的指针域指向头节点的下一个节点(把L的NULL复制给新节点)
L->next = p; // 头节点的指针域指向新节点
return 1; // 返回1表示成功
}
/* 单链表 - 遍历 */
void TraverseList(Node *L)
{
Node *p = L->next; // 从头节点的下一个节点开始遍历
while (p != NULL) // 遍历到链表末尾
{
printf("%d ", p->data); // 输出节点的数据域
p = p->next; // 移动到下一个节点
}
printf("\n"); // 换行
}
/* 单链表 - 尾插法 */
// 获取尾节点地址
Node *GetTail(Node *List)
{
Node *p = List; // 从头节点开始遍历
while (p->next != NULL) // 遍历到链表末尾
{
p = p->next; // 移动到下一个节点
}
return p; // 返回尾节点
}
/**
* @Description:单链表 - 尾插法插入数据
* @param {Node} *tail 尾节点
* @param {ElemType} e 插入的数据
* @return {*} 返回新的尾节点
*/
Node *InsertTail(Node *tail, ElemType e)
{
Node *p = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
p->data = e; // 在新节点的数据域存入数据e
tail->next = p; // 尾节点的指针域指向新节点
p->next = NULL; // 新节点的指针域为空
return p; // 返回新的尾节点
}
/**
* @Description:单链表 - 在指定位置插入数据
* @param {Node} *L 单链表的头节点
* @param {int} pos 位置
* @param {ElemType} e 插入的数据
* @return {*}
*/
int InsertPosNode(Node *L, int pos, ElemType e)
{
// 用来保存插入位置的前驱节点
Node *p = L; // 从头节点开始遍历
int i = 0;
// 遍历链表-找到插入位置的前驱节点
while (i < pos - 1) // 遍历到插入位置的前驱节点
{
p = p->next; // 移动到下一个节点
i++;
if (p == NULL) // 判断是否到达链表末尾
{
printf("插入位置不合法\n");
return 0;
}
}
Node *newnode = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
newnode->data = e; // 在新节点的数据域存入数据e
newnode->next = p->next; // 新节点的指针域指向插入位置的前驱节点的下一个节点
p->next = newnode; // 插入位置的前驱节点的指针域指向新节点
return 1;
}
/**
* @Description:单链表 - 删除指定位置的节点
* @param {Node} *L 单链表的头节点
* @param {int} pos 位置
* @return {*} 返回1表示成功
*/
int DeletePosNode(Node *L, int pos)
{
// 用来保存删除位置的前驱节点
Node *p = L; // 从头节点开始遍历
int i = 0;
// 遍历链表-找到删除节点的前驱节点
while (i < pos - 1) // 遍历到删除位置的前驱节点
{
p = p->next; // 移动到下一个节点
i++;
if (p == NULL) // 判断是否到达链表末尾
{
printf("删除位置不合法\n");
return 0;
}
}
if (p->next == NULL) // 判断删除位置是否合法
{
printf("删除位置不合法\n");
return 0;
}
Node *q = p->next; // 保存要删除的节点的地址
p->next = q->next; // 删除节点的前驱节点的指针域 指向 删除节点的下一个节点
free(q); // 释放删除节点的内存
return 1; // 返回1表示成功
}
int main()
{
Node *list = InitList(); // 初始化一个单链表
printf("头插法插入数据1 2 3\n");
InsertHead(list, 1); // 头插法插入数据1
InsertHead(list, 2); // 头插法插入数据2
InsertHead(list, 3); // 头插法插入数据3
printf("遍历-链表中的元素为:");
TraverseList(list); // 遍历单链表
printf("\n"); // 换行
printf("\n尾插法插入数据4 5 6\n");
Node *tail = GetTail(list); // 获取尾节点地址
tail = InsertTail(tail, 4); // 尾插法插入数据4
tail = InsertTail(tail, 5); // 尾插法插入数据5
tail = InsertTail(tail, 6); // 尾插法插入数据6
printf("遍历-链表中的元素为:");
TraverseList(list); // 遍历单链表
printf("\n在指定位置3插入数据7\n");
InsertPosNode(list, 3, 7); // 在指定位置3插入数据7
printf("遍历-链表中的元素为:");
TraverseList(list); // 遍历单链表
printf("\n删除指定位置3的节点\n");
DeletePosNode(list, 3); // 删除指定位置3的节点
printf("遍历-链表中的元素为:");
TraverseList(list); // 遍历单链表
return 0;
}
输出结果:
c
头插法插入数据1 2 3
遍历-链表中的元素为:3 2 1
尾插法插入数据4 5 6
遍历-链表中的元素为:3 2 1 4 5 6
在指定位置3插入数据7
遍历-链表中的元素为:3 2 7 1 4 5 6
删除指定位置3的节点
遍历-链表中的元素为:3 2 1 4 5 6
请按任意键继续. . .
获取链表长度
c
int GetListLength(Node *L)
{
int length = 0;
Node *p = L->next; // 从头节点的下一个节点开始遍历,头节点不算在内
while (p != NULL)
{
p = p->next;
length++;
}
return length;
}
从头节点的下一个节点开始遍历,头节点不算在内
释放链表
保存头节点,把剩下的每一个节点的内存都释放掉
- 指针p指向头节点后的第一个节点
- 判断指针p是否指向NULL
- 如果p不为NULL,用指针q记录指针p的后继结点
- 释放指针p指向的节点
- 指针p和指针q指向同一个节点,循环上面的操作
释放链表代码
c
void FreeList(Node *L)
{
Node *p = L->next; // 从头节点的下一个节点开始遍历,头节点不需要释放
Node *q = NULL; // 用来保存下一个节点的地址,q能掌握下一个节点的地址,这是灵魂所在
while (p != NULL)
{
q = p->next; // 保存下一个节点的地址
free(p); // 释放当前节点的内存
p = q; // 移动到下一个节点
}
L->next = NULL; // 头节点的指针域为空
}
完整代码如下:
c
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType; // 定义元素类型
typedef struct node // 定义节点类型
{
ElemType data;
struct node *next;
} Node;
/* 初始化一个单链表-造一个头节点 */
Node *InitList()
{
Node *head = (Node *)malloc(sizeof(Node)); // 为头节点分配内存
head->data = 0; // 头节点的数据域为0
head->next = NULL; // 头节点的指针域为空
return head; // 返回头节点
}
/*单链表 - 头插法*/
int InsertHead(Node *L, ElemType e)
{
Node *p = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
p->data = e; // 在新节点的数据域存入数据e
p->next = L->next; // 新节点的指针域指向头节点的下一个节点(把L的NULL复制给新节点)
L->next = p; // 头节点的指针域指向新节点
return 1; // 返回1表示成功
}
/* 单链表 - 遍历 */
void TraverseList(Node *L)
{
Node *p = L->next; // 从头节点的下一个节点开始遍历
while (p != NULL) // 遍历到链表末尾
{
printf("%d ", p->data); // 输出节点的数据域
p = p->next; // 移动到下一个节点
}
printf("\n"); // 换行
}
/* 单链表 - 尾插法 */
// 获取尾节点地址
Node *GetTail(Node *List)
{
Node *p = List; // 从头节点开始遍历
while (p->next != NULL) // 遍历到链表末尾
{
p = p->next; // 移动到下一个节点
}
return p; // 返回尾节点
}
/**
* @Description:单链表 - 尾插法插入数据
* @param {Node} *tail 尾节点
* @param {ElemType} e 插入的数据
* @return {*} 返回新的尾节点
*/
Node *InsertTail(Node *tail, ElemType e)
{
Node *p = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
p->data = e; // 在新节点的数据域存入数据e
tail->next = p; // 尾节点的指针域指向新节点
p->next = NULL; // 新节点的指针域为空
return p; // 返回新的尾节点
}
/**
* @Description:单链表 - 在指定位置插入数据
* @param {Node} *L 单链表的头节点
* @param {int} pos 位置
* @param {ElemType} e 插入的数据
* @return {*}
*/
int InsertPosNode(Node *L, int pos, ElemType e)
{
// 用来保存插入位置的前驱节点
Node *p = L; // 从头节点开始遍历
int i = 0;
// 遍历链表-找到插入位置的前驱节点
while (i < pos - 1) // 遍历到插入位置的前驱节点
{
p = p->next; // 移动到下一个节点
i++;
if (p == NULL) // 判断是否到达链表末尾
{
printf("插入位置不合法\n");
return 0;
}
}
Node *newnode = (Node *)malloc(sizeof(Node)); // 创建一个新的节点
newnode->data = e; // 在新节点的数据域存入数据e
newnode->next = p->next; // 新节点的指针域指向插入位置的前驱节点的下一个节点
p->next = newnode; // 插入位置的前驱节点的指针域指向新节点
return 1;
}
/**
* @Description:单链表 - 删除指定位置的节点
* @param {Node} *L 单链表的头节点
* @param {int} pos 位置
* @return {*} 返回1表示成功
*/
int DeletePosNode(Node *L, int pos)
{
// 用来保存删除位置的前驱节点
Node *p = L; // 从头节点开始遍历
int i = 0;
// 遍历链表-找到删除节点的前驱节点
while (i < pos - 1) // 遍历到删除位置的前驱节点
{
p = p->next; // 移动到下一个节点
i++;
if (p == NULL) // 判断是否到达链表末尾
{
printf("删除位置不合法\n");
return 0;
}
}
if (p->next == NULL) // 判断删除位置是否合法
{
printf("删除位置不合法\n");
return 0;
}
Node *q = p->next; // 保存要删除的节点的地址
p->next = q->next; // 删除节点的前驱节点的指针域 指向 删除节点的下一个节点
free(q); // 释放删除节点的内存
return 1; // 返回1表示成功
}
int GetListLength(Node *L)
{
int length = 0;
Node *p = L; // 从头节点开始遍历,头节点算在内
while (p != NULL)
{
p = p->next;
length++;
}
return length;
}
void FreeList(Node *L)
{
Node *p = L->next; // 从头节点的下一个节点开始遍历,头节点不需要释放
Node *q = NULL; // 用来保存下一个节点的地址,q能掌握下一个节点的地址,这是灵魂所在
while (p != NULL)
{
q = p->next; // 保存下一个节点的地址
free(p); // 释放当前节点的内存
p = q; // 移动到下一个节点
}
L->next = NULL; // 头节点的指针域为空
}
int main()
{
Node *list = InitList(); // 初始化一个单链表
printf("头插法插入数据1 2 3\n");
InsertHead(list, 1); // 头插法插入数据1
InsertHead(list, 2); // 头插法插入数据2
InsertHead(list, 3); // 头插法插入数据3
printf("尾插法插入数据4 5 6\n");
Node *tail = GetTail(list); // 获取尾节点地址
tail = InsertTail(tail, 4); // 尾插法插入数据4
tail = InsertTail(tail, 5); // 尾插法插入数据5
tail = InsertTail(tail, 6); // 尾插法插入数据6
printf("在指定位置3插入数据7\n");
InsertPosNode(list, 3, 7); // 在指定位置3插入数据7
printf("删除指定位置3的节点\n");
DeletePosNode(list, 3); // 删除指定位置3的节点
printf("遍历-链表中的元素为:");
TraverseList(list); // 遍历单链表
printf("链表长度为%d\n", GetListLength(list));
printf("释放链表\n");
FreeList(list); // 释放链表的内存
printf("链表长度为%d\n", GetListLength(list));
return 0;
}
输出
c
头插法插入数据1 2 3
尾插法插入数据4 5 6
在指定位置3插入数据7
删除指定位置3的节点
遍历-链表中的元素为:3 2 1 4 5 6
链表长度为7
释放链表
链表长度为1
请按任意键继续. . .