链表
- 一、链表的定义
- 二、专业术语
- 三、注意
- 四、链表的分类
- 五、算法
- 六、完整代码
一、链表的定义
n个节点离散分配
彼此通过指针相连
每个节点只有一个前驱结点、一个后续节点。首节点没有前驱结点,尾节点没有后续节点。
二、专业术语
首节点:第一个有效节点
尾节点:最后一个有效节点
头节点:首节点之前的节点,没有实际含义(不存放有效数据)的节点,为了方便链表操作。
头结点的数据域可以不存储任何信息,也可存储与数据元素类型相同的其他附加信息。例如,当数据元素为整数型时,头结点的数据域中可存放该线性表的长度。
作用:
(1)便于首元结点的处理
(2)便于空表和非空表的统一处理
头指针:指向头节点的指针变量
尾指针:指向尾节点的指针变量
三、注意
1.如果希望通过一个函数来对列表进行处理,我们至少需要接收链表的哪些参数?
一个参数:头指针
通过头指针可以推出链表的其他所有信息。
单链表可由头指针唯一确定
四、链表的分类
单链表
单链表是非随机存取的存储结构,要取得第l个数据元素必须从头指针出发顺链进行寻找,也称为顺序存取的存取结构。因此,其基本操作的实现不同于顺序表。
双链表:每个节点有两个指针域,一个指向前个节点,一个存放后个节点的地址。
循环链表:能通过任何一个节点找到其他所有的节点。最后一个节点指向首节点。
非循环链表
五、算法
遍历
查找
清空
销毁
求链表长度
排序
删除节点
插入节点
1.创建链表节点结构体
单链表中每个结点的存储结构:
#include <stdio.h>
struct Node
{
int data;//数据域
struct Node* pNext;//指针域
}NODE, * PNODE;//*PNODE 相当于struct Node*;NODE相当于struct Node
为了提高程序的可读性,在此对同一结构体指针类型起了两个名称,PNODE与NODE* , 两者本质上是等价的。通常习惯上用PNODE定义单链表,强调定义的是某个单链表的头指针;用NODE*定义指向单链表中任意结点的指针变量。
2、创建空节点
创建一个节点pHead赋null,后续会创建一个链表,并将链表的头节点的地址赋给pHead。
3.创建链表 create_list()
函数类型:PNODE
作用:创建一个链表,并将链表的头节点的地址赋给pHead。
参数:无 void
返回值:节点类型的地址
思路:
定义两个节点指针动态开空间:一个pHead在函数刚开始创建当做链表的头节点,一个pNew在循环里动态创建当做每次新创建的节点。
定义一个节点指针pTail使它永远指向尾节点(方便新加节点每次加载最后一个节点后面)
步骤
1.动态创建一个pHead当做链表的头节点。并判断是否创建成功。
int len;//有效结点的个数,链表长度
int i;
int val;//临时存放用户输入的节点的数据值
PNODE pHead = (PNODE)malloc(sizeof(NODE));
if (NULL == pHead)
{
printf("分配失败!程序终止!");
exit(-1);
}
printf("请输入您需要生成的链表节点的个数:len=");
scanf("%d", &len);
2.在循环里动态创建一个pNew当做每次链表新添加的节点。并判断是否创建成功。
for ( i = 0; i < len; i++)
{
printf("请输入第%d个节点的值:", i + 1);
scanf("%d", &val);
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if (NULL == pNew)
{
printf("分配失败!程序终止!");
exit(-1);
}
}
3.将新节点挂载链表的最尾端。此时需要一个节点指针pTail 永远指向链表的最后一个节点。该节点指针一开始指向头结点。每次创建完新节点,就将新节点挂在pTail后面,新节点pNext为null,然后该指针再指向新节点。pTail指向成为最后一个节点,便于下次添加节点添加在尾节点后面
PNODE pTail = pHead;
pTail->pNext = NULL;
for ( i = 0; i < len; i++)
{
printf("请输入第%d个节点的值:", i + 1);
scanf("%d", &val);
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if (NULL == pNew)
{
printf("分配失败!程序终止!");
exit(-1);
}
pNew->data = val;//数据赋给pNew
pTail->pNext = pNew;
pNew->pNext = NULL;
pTail = pNew;//pTail指向成为最后一个节点,便于下次添加节点添加在尾节点后面
}
完整代码
/*
函数类型:PNODE
作用:创建一个链表,并将链表的头节点的地址赋给pHead。
参数:无 void
返回值:节点类型的地址
*/
PNODE create_list(void)
{
int len;//有效结点的个数,链表长度
int i;
int val;//临时存放用户输入的节点的数据值
PNODE pHead = (PNODE)malloc(sizeof(NODE));
PNODE pTail = pHead;
pTail->pNext = NULL;
if (NULL == pHead)
{
printf("分配失败!程序终止!");
exit(-1);
}
printf("请输入您需要生成的链表节点的个数:len=");
scanf("%d", &len);
for (i = 0; i < len; i++)
{
printf("请输入第%d个节点的值:", i + 1);
scanf("%d", &val);
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if (NULL == pNew)
{
printf("分配失败!程序终止!");
exit(-1);
}
pNew->data = val;//数据赋给pNew
pTail->pNext = pNew;
pNew->pNext = NULL;
pTail = pNew;//pTail指向成为最后一个节点,便于下次添加节点添加在尾节点后面
}
return pHead;
}
4.遍历链表 traverse_list()
作用:遍历打印整个链表的值
参数: PNODE pHead 链表头节点
返回值:无
思路:一个节点指针为头节点next,如果p为空,说明该链表为空。p不为null,就打印当前节点数据值,并向后偏移,知道尾节点。遍历完毕。
//指针不断偏移
void traverse_list(PNODE pHead)
{
PNODE p = pHead->pNext;
while (NULL!=p)
{
printf("%d ", p->data);
p = p->pNext;
}
printf("\n");
}
main测试:
int main(void)
{
//struct Node* pHead = NULL;
PNODE pHead = NULL;
pHead = create_list();//创建一个非循环链表,并将链表的头节点的地址赋给pHead
traverse_list(pHead);
return 0;
}
结果:
5.判断链表是否为空(内存满–不太可能)is_empty(PNODE pHead)
函数类型:bool
作用:判断链表是否为空
参数:无 void
返回值:节点类型的地址
思路:链表头节点指针域为NULL,就代表该链表为空链表。
#include<stdbool.h>//bool:true\false
bool is_empty(PNODE pHead)
{
if (NULL == pHead->pNext)
{
return true;
}
else
{
return false;
}
}
6.链表长度 int length_list(PNODE pHead)
函数类型:PNODE
作用:链表长度
参数:PNODE
返回值:节点类型的地址
思路:定义一个int值len记录链表有效节点个数,让一个节点p赋值pHead->pNext,如果p不为NULL,则len+1.知道最后一个节点,然后返回len。
代码
int length_list(PNODE pHead)
{
int len=0;
PNODE p = pHead->pNext;
while (NULL!=p)
{
++len;
p = p->pNext;
}
return len;
}
main测试:
int main(void)
{
//struct Node* pHead = NULL;
PNODE pHead = NULL;
pHead = create_list();//创建一个非循环链表,并将链表的头节点的地址赋给pHead
traverse_list(pHead);
if (is_empty(pHead))
{
printf("该链表为空!\n");
}
else
{
printf("该链表bu为空!\n");
}
int len = length_list(pHead);
printf("该链表的长度为%d\n", len);
return 0;
}
结果:
7. 插入节点 bool insert_list(PNODE pHead、int pos,int var)
函数类型:bool
作用:在p->pHead所指向的链表的第pos个节点的前面插入一个新的节点。该节点的值为var。
参数:PNODE pHead、int ,int
返回值:true false
思路(伪代码)
p是链表中一个节点,现要将一个节点q插入到p节点。
① q的指针域存放p的下个节点的地址*(也就是原先p的指针域存放的),然后p的指针域再存放q的地址。顺序不能颠倒。
q->next=p->next,p->next=q
② 借助一个节点r,先存放原先p的下个节点的地址(也就是原先p的指针域存放的),然后p的指针域再存放q的地址,q的指针域再存放r。
r=p->next,p->next=q,q->next=r
步骤
- 创建一个节点赋pHead
- 遍历节点指针直到待插入位置pos的前一个节点,跳出循环
- 此时新动态创建一个节点pNew
- 将var赋给pNew的data 新创建一个节点q保存原先pos的上个节点的地址
- p的next指向新节点pNew
- 新节点pNew的next指向q(原先pos的上个节点的地址)
完毕,此时新插入的节点就是该链表的第pos个节点啦
代码
bool insert_list(PNODE pHead, int pos, int val)
{
int i = 0;
PNODE p = pHead;
while (NULL != p && i < pos - 1)
{
p = p->pNext;
i++;
}
if (i > pos - 1 || NULL == p)
{
return false;
}
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if (NULL == pNew)
{
printf("insert_list pNew动态分配内存失败!");
exit(-1);
}
pNew->data = val;
PNODE q = p->pNext;
p->pNext = pNew;
pNew->pNext = q;
return true;
}
main测试:
int main(void)
{
//struct Node* pHead = NULL;
PNODE pHead = NULL;
pHead = create_list();//创建一个非循环链表,并将链表的头节点的地址赋给pHead
traverse_list(pHead);
insert_list(pHead, 4, 45);
traverse_list(pHead);
/*if (is_empty(pHead))
{
printf("该链表为空!\n");
}
else
{
printf("该链表bu为空!\n");
}
int len = length_list(pHead);
printf("该链表的长度为%d\n", len);
sort_list(pHead);
traverse_list(pHead);*/
return 0;
}
结果:
8.删除节点 bool delete_list(PNODE pHead、int pos,int*val)
函数类型:bool
作用:删除节点
参数:PNODE pHead、int pos【删除第几个节点】 ,int*val【将删除的数值】
返回值:true false
free p相当于删除p指向节点所占的内存,不是删除p本身。 p->pNext;相当于p所指向结构体变量中的pNext成员本身
思路(伪代码)
删除p所指节点的后面节点:
猜测1:不能直接将p->pNext=p->pNext->pNext;(p指向原先p后面节点的后面节点)。不然会导致原先p后面节点(待删除节点内存泄漏)
猜测2:free p->pNext; 就找不到后面的节点了
正解:
定义一个节点r保存带删除节点也就是r=p->pNext;
p指向要删除节点后面的那个节点:p->pNext=p->pNext->pNext;
释放掉删除节点的空间 free ( r );
代码
bool delete_list(PNODE pHead, int pos, int* val)
{
int i = 0;
PNODE p = pHead;
while (NULL != p->pNext && i < pos - 1)
{
p = p->pNext;
i++;
}
if (i > pos - 1 || NULL == p->pNext)
{
return false;
}
//将删除的值保存下来
PNODE q = p->pNext;
*val = q->data;
//删除某个节点
p->pNext = p->pNext->pNext;
free(q);
q = NULL;
return true;
}
main测试
int main(void)
{
//struct Node* pHead = NULL;
int val;
PNODE pHead = NULL;
pHead = create_list();//创建一个非循环链表,并将链表的头节点的地址赋给pHead
traverse_list(pHead);
insert_list(pHead, 4, 45);
traverse_list(pHead);
delete_list(pHead, 3, &val);
printf("您删除的数值为%d\n", val);
traverse_list(pHead);
/*if (is_empty(pHead))
{
printf("该链表为空!\n");
}
else
{
printf("该链表bu为空!\n");
}
int len = length_list(pHead);
printf("该链表的长度为%d\n", len);
sort_list(pHead);
traverse_list(pHead);*/
return 0;
}
结果:
9.排序 void sort_list(PNODE pHead)
函数类型:void
作用:将链表各节点数据进行比较排序,也就是数值最小的存在第一个节点里,以此类推。
参数:PNODE pHead
返回值:无
思路:冒泡排序算法。思路方法和数组排序一样。
i=0相当于i=pHead->pNext;
i++相当于i=i->pNext;
j = i+1相当于 j=i->pNext
a[i]>a[j] 相当于i->data > j->data
代码
数组排序:
int i, j, t;
//int len=length_list(pHead);
int len = 5;
for ( i = 0; i < len; i++)
{
for ( j = i+1; j < len; j++)
{
if (a[i]>a[j])
{
t = a[i];
a[i] = a[j];
a[j] = t;
}
}
}
链表排序:
void sort_list(PNODE pHead)
{
int i, j, t;
PNODE p, q;
int len = length_list(pHead);
//int len = 5;
for (i = 0, p = pHead->pNext; i < len - 1; i++, p = p->pNext)
{
for (j = i + 1, q = p->pNext; j < len; j++, q = q->pNext)
{
if (p->data > q->data)
{
t = p->data;//t = a[i];
p->data = q->data;// a[i] = a[j];
q->data = t;
}
}
}
return;
}
man测试
int main(void)
{
//struct Node* pHead = NULL;
PNODE pHead = NULL;
pHead = create_list();//创建一个非循环链表,并将链表的头节点的地址赋给pHead
traverse_list(pHead);
if (is_empty(pHead))
{
printf("该链表为空!\n");
}
else
{
printf("该链表bu为空!\n");
}
int len = length_list(pHead);
printf("该链表的长度为%d\n", len);
sort_list(pHead);
traverse_list(pHead);
return 0;
}
结果:
六、完整代码
#pragma warning(disable:4996)
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include<stdbool.h>//bool:true\false
typedef struct Node
{
int data;//数据域
struct Node* pNext;//指针域
}NODE, * PNODE;//*PNODE 相当于struct Node*;NODE相当于struct Node
PNODE create_list(void);
void traverse_list(PNODE pHead);
bool is_empty(PNODE pHead);
int length_list(PNODE pHead);
bool insert_list(PNODE pHead, int pos, int val);
bool delete_list(PNODE pHead, int pos, int* val);
void sort_list(PNODE pHead);
int main(void)
{
//struct Node* pHead = NULL;
int val;
PNODE pHead = NULL;
pHead = create_list();//创建一个非循环链表,并将链表的头节点的地址赋给pHead
traverse_list(pHead);
insert_list(pHead, 4, 45);
traverse_list(pHead);
delete_list(pHead, 3, &val);
printf("您删除的数值为%d\n", val);
traverse_list(pHead);
/*if (is_empty(pHead))
{
printf("该链表为空!\n");
}
else
{
printf("该链表bu为空!\n");
}
int len = length_list(pHead);
printf("该链表的长度为%d\n", len);
sort_list(pHead);
traverse_list(pHead);*/
return 0;
}
/*
函数类型:PNODE
作用:创建一个链表,并将链表的头节点的地址赋给pHead。
参数:无 void
返回值:节点类型的地址
*/
PNODE create_list(void)
{
int len;//有效结点的个数,链表长度
int i;
int val;//临时存放用户输入的节点的数据值
PNODE pHead = (PNODE)malloc(sizeof(NODE));
PNODE pTail = pHead;
pTail->pNext = NULL;
if (NULL == pHead)
{
printf("分配失败!程序终止!");
exit(-1);
}
printf("请输入您需要生成的链表节点的个数:len=");
scanf("%d", &len);
for (i = 0; i < len; i++)
{
printf("请输入第%d个节点的值:", i + 1);
scanf("%d", &val);
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if (NULL == pNew)
{
printf("分配失败!程序终止!");
exit(-1);
}
pNew->data = val;//数据赋给pNew
pTail->pNext = pNew;
pNew->pNext = NULL;
pTail = pNew;//pTail指向成为最后一个节点,便于下次添加节点添加在尾节点后面
}
return pHead;
}
//指针不断偏移
void traverse_list(PNODE pHead)
{
PNODE p = pHead->pNext;
while (NULL != p)
{
printf("%d ", p->data);
p = p->pNext;
}
printf("\n");
}
bool is_empty(PNODE pHead)
{
if (NULL == pHead->pNext)
{
return true;
}
else
{
return false;
}
}
int length_list(PNODE pHead)
{
int len = 0;
PNODE p = pHead->pNext;
while (NULL != p)
{
++len;
p = p->pNext;
}
return len;
}
bool insert_list(PNODE pHead, int pos, int val)
{
int i = 0;
PNODE p = pHead;
while (NULL != p && i < pos - 1)
{
p = p->pNext;
i++;
}
if (i > pos - 1 || NULL == p)
{
return false;
}
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if (NULL == pNew)
{
printf("insert_list pNew动态分配内存失败!");
exit(-1);
}
pNew->data = val;
PNODE q = p->pNext;
p->pNext = pNew;
pNew->pNext = q;
return true;
}
bool delete_list(PNODE pHead, int pos, int* val)
{
int i = 0;
PNODE p = pHead;
while (NULL != p->pNext && i < pos - 1)
{
p = p->pNext;
i++;
}
if (i > pos - 1 || NULL == p->pNext)
{
return false;
}
//将删除的值保存下来
PNODE q = p->pNext;
*val = q->data;
//删除某个节点
p->pNext = p->pNext->pNext;
free(q);
q = NULL;
return true;
}
void sort_list(PNODE pHead)
{
int i, j, t;
PNODE p, q;
int len = length_list(pHead);
//int len = 5;
for (i = 0, p = pHead->pNext; i < len - 1; i++, p = p->pNext)
{
for (j = i + 1, q = p->pNext; j < len; j++, q = q->pNext)
{
if (p->data > q->data)
{
t = p->data;//t = a[i];
p->data = q->data;// a[i] = a[j];
q->data = t;
}
}
}
return;
}