链表学习笔记
===================================
第1章 单向链表
1.1 链表和数组的区别
数组:数组一次性分配一块连续的存储区域。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H4eQfSQz-1679385090568)(null)]
优点:随机访问元素效率高。
缺点:1️⃣在指定位置上插入和删除会导致元素大量移动(效率低)。(根本原因:由于数组是一块内存空间导致的。)2️⃣需要分配一块连续的存储区域。(如果区域很大,可能分配失败。)(分配过大,空间浪费;分配较小,空间不够)
链表:无需一次性分配一块连续的存储区域,只需分配n块节点区域(随机),通过指针建立关系。
优点:1️⃣不需要一块连续的存储区域。2️⃣插入和删除元素时的效率高。
缺点:随机访问元素效率低。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XBvLwure-1679385094614)(null)]
链表是有一系列节点组成的,每个节点包含两个域,一个数据域; 一个指针域,保存下一个节点的地址。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hnTnQGSj-1679385091985)(null)]
链表在内存中是非连续的。
链表在指定位置插入和删除元素不需要移动元素,只需要修改指针即可。在查找元素时,效率比数组低。
链表相对于数组,多了指针域的开销。
1.2 链表分类
单向链表:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U9nmrjyM-1679385090604)(null)]
单向循环链表:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zFVs9aTP-1679385091404)(null)]
双向链表:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gtt5WfD1-1679385091417)(null)]
双向循环链表:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-amisSnD7-1679385094214)(null)]
*拿到链表的第一个节点就相当于拿到了整个链表。
头节点:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RNZN6ydr-1679385090589)(null)]
头节点不保存任何数据。
如果没有头节点,在插入数据时,需要判断,是不是插入在第一个节点,如果插入在第一个节点,需要进行特殊处理(更新第一个节点,因为知道了第一个节点,就知道了整个链表。)
如果有头节点,在插入数据时,因为头节点保持不变,头节点永远是第一个节点。插入的节点,紧接着头节点,因此就不需要进行判断插入的位置是不是头节点。(链表带头节点比不带头节点少判断一种情况,)
链表带头节点比不带头节点少判断一种情况(头部插入的情况)。
next指针类型为当前节点类型,是节点本身类型的指针。
链表中的每一个节点都是同一类型的。
1.2 静态链表
定义:所有节点都是在程序中定义的,不是临时开辟的,用完也不用释放,这种链表称为“静态链表”。
程序:
```c #include//链表节点类型
struct LinkNode
{
int data;
struct LinkNode *next;
};
//测试函数
void test()
{
struct LinkNode node1 = {10,NULL};
struct LinkNode node2 = {20,NULL};
struct LinkNode node3 = {30,NULL};
struct LinkNode node4 = {40,NULL};
struct LinkNode node5 = {50,NULL};
struct LinkNode node6 = {60,NULL};
node1.next = &node2;
node2.next = &node3;
node3.next = &node4;
node4.next = &node5;
node5.next = &node6;
//遍历链表
struct LinkNode* pCurrent = &node1;
while (pCurrent != NULL)
{
printf("%d\n",pCurrent->data);
//指针移动到下一个元素的首地址
pCurrent = pCurrent->next;
}
}
int main()
{
test();
system("pause");
return 0;
}
### 1.3 动态链表
<p style="color: black;font-size: 18px; font-family:'楷体';">定义:在程序执行过程中建立一个链表,即一个一个地开辟节点和输入各节点数据,并建立起前后相连的关系。</p>
#### 1.3.1 初始化链表
<p style="color: black;font-size: 18px; font-family:'楷体';">程序:</p>
```C
struct LinkNode *Init_LinkList()
{
//创建头节点
struct LinkNode *header = (struct LinkNode *)malloc(sizeof(struct LinkNode));
header->data = -1;
header->next = NULL;
//尾部指针
struct LinkNode* pRear=header;
int val = -1;
while(true)
{
printf("输入插入数据:\n");
scanf("%d",&val);
if (val == -1)
{
break;
}
//创建新节点
struct LinkNode* newnode = (struct LinkNode*)malloc(sizeof(struct LinkNode));
newnode->data = val;
newnode->next = NULL;
//新节点插入到链表中
pRear->next = newnode;
//更新尾部指针走向
pRear = newnode;
}
return header;
}
1.3.2 插入节点
程序:
//在值为oldval的位置插入一个新的数据newval 查找->插入
void InsertByValue_LinkList(struct LinkNode *header, int oldval, int newval)
{
if(NULL == header)
{
return;
}
//复制指针变量
struct LinkNode* pPrev = header;
struct LinkNode* pCurrent = pPrev->next;
while (pCurrent != NULL)
{
if (pCurrent->data == oldval)
{
break;
}
pPrev = pCurrent;
pCurrent = pCurrent->next;
}
//如果该值不存在直接插入在链表尾部
#if 0
//如果pCurrent为NULL,说明链表中不存在值为oldval的节点,直接返回
if (pCurrent == NULL)
{
return;
}
#endif
//创建新节点节点
struct LinkNode* newnode = (struct LinkNode *)malloc(sizeof(struct LinkNode));
newnode->data = newval;
newnode->next = NULL;
//新节点插入到链表中
newnode->next = pCurrent;
pPrev->next = newnode;
}
1.3.2 删除节点
程序:
//删除值为delval的节点
void RemoveByValue_LinkList(struct LinkNode *header, int delval)
{
//链表不存在时
if (NULL == header)
{
return;
}
//辅助指针变量
struct LinkNode *pPrev = header;
struct LinkNode *pCurrent = pPrev->next;
while (pCurrent != NULL)
{
if (pCurrent->data== delval)
{
break;
}
//移动两个辅助指针
pPrev = pCurrent;
pCurrent = pCurrent->next;
}
if (NULL == pCurrent)
{
return;
}
//重新建立待删除节点的前驱和后继节点的关系
pPrev->next = pCurrent->next;
//释放删除节点内存
free(pCurrent);
pCurrent = NULL;
}
1.3.4 遍历链表
程序:
//遍历
void Foreach_LinkList(struct LinkNode *header)
{
if (NULL == header)
{
return;
}
//辅助指针变量
struct LinkNode* pCurrent = header->next;
while (pCurrent != NULL)
{
printf("%d",pCurrent->data);
pCurrent = pCurrent->next;
}
}
1.3.5 销毁链表
程序:
void Destroy_LinkList(struct LinkNode *header)
{
if (NULL == header)
{
return;
}
//辅助指针变量
struct LinkNode* pCurrent = header->next;
while (pCurrent != NULL)
{
//先保存当前节点的下一个节点地址
struct LinkNode* pNext = pCurrent->next;
//释放当前节点
free(pCurrent);
//指针向后移动
pCurrent = pNext;
}
}
1.3.6 清空链表
程序:
void Clear_LinkList(struct LinkNode *header)
{
if (NULL == header)
{
return;
}
//辅助指针标量
struct LinkNode *pCurrent = header->next;
while (pCurrent != NULL)
{
//先保存当前节点的下一个节点地址
struct LinkNode *pNext = pCurrent->next;
//释放当前节点内存
free(pCurrent);
//pCurrent指向下一个节点
pCurrent = pNext;
}
header->next = NULL;
}