1.对链表的认识、预处理:
链表,别名链式存储结构或单链表,用于存储逻辑关系为 "一对一" 的数据。与s顺序表不同,链表不限制数据的物理存储状态,换句话说,使用链表存储的数据元素,其物理存储位置是随机的。
画图认知:
通过指针来找到链表的下一个元素的位置,同时我们创建链表的时候用的也是指针,取名为头指针,头指针也是一种指针,这个时候就会出现对链表认知的一个难点,头指针指向的是这个链表头部位置,头部位置里面有也放着一个指针指向下一个元素,大体如图:
这一点需要好好理解,区分二者的区别
为了方便后续书写,在“Slist”头文件里面进行以下预处理:
#pragma once
#include<stdio.h>
#include<stdlib.h>
typedef int SLTDataType;
struct SLTNode
{
SLTDataType Data;
struct SLTNode* next;
};
typedef struct SLTNode SL;
2.链表的尾插:
首先我们要创建一个链表,起名为Plist,并且在初始化的时候赋值为空,方便后续头插。
显然,再进行插入的时候,需要对其是否为空进行检验,如果为空,则可以直接赋值,不然,就要一直找到链表的尾巴,这时候我们可以创建一个指针,把链表起点地址赋给它,然后一一遍历,直到它指向的next是空指针,说明现在它就是链表的尾巴,然后就可以进行操作。
void SLTPushBack(SL** PPhead, SLTDataType x)
{
SL* newnode = CreatSLTNode(x);
if (*PPhead == NULL)
{
*PPhead = newnode;
}
else
{
SL* tail = *PPhead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
关于CreatSLTNode()的解释:
由于创建新的节点用的比较多,可以直接把他封装成一个函数便于使用:
SL* CreatSLTNode(SLTDataType x)
{
SL* newnode = (SL*)malloc(sizeof(SL));
if (newnode == NULL)
{
printf("malloc failed\n");
return;
}
newnode->Data = x;
newnode->next = NULL;
return newnode;
}
3.链表的头插:
头插与尾插相比简单不少,只需要创建一个新节点newnode,然后把newnode的下一个节点指针赋值为*PPhead,然后把头指针,也就是*PPhead改成newnode的地址即可:
void SLTPushFront(SL** PPhead, SLTDataType x)
{
SL* newnode = CreatSLTNode(x);
newnode->next = *PPhead;
*PPhead = newnode;
}
4.链表的头删:
头删只需把,先把下一个结点的位置记录下来,头指针赋值成下一个节点的位置,然后free掉头指针,最后把头指针赋值成原先记录好的位置即可。
void SLTPopFront(SL** PPhead)
{
SL* next = (*PPhead)->next;
free(*PPhead);
*PPhead = next;
}
5.链表的尾删:
尾删相对来说复杂一些:
1.需要考虑链表是非为空
2.需要考虑是否只有一个元素
3.怎么找打尾元素的前一个元素?
如此,我们可以用两个指针变量,一个prev,另一个tail,来采取类似快慢指针的方式去寻找
void SLTPopBack(SL** PPhead)
{
if (*PPhead == NULL)
{
return;
}
else if ((*PPhead)->next == NULL)
{
free(*PPhead);
*PPhead = NULL;
}
else
{
SL* prev = NULL;
SL* tail = *PPhead;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
free(tail);
prev->next = NULL;
}
}
6.链表的查找,插入,删除指定元素:
查找就非常简单,一一遍历即可:
SL* SLTFindData(SL* Phead, SLTDataType x)
{
SL* cur = Phead;
while (cur != NULL)
{
if (cur->Data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
插入,删除需要注意的一点:如果是头、尾元素,那怎么办?再写一遍?
NO! 这样不就和头删尾删一样了吗?直接复制粘贴~
在这其中有一些细节一代略过,比如链表之间的链接之类的,都写在代码里了
void SLTInsert(SL** PPhead, SL* pos, SLTDataType x)
{
if (pos == *PPhead)
{
SLTPushFront(PPhead, x);
}
else
{
SL* newnode = CreatSLTNode(x);
SL* prev = *PPhead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = newnode;
newnode->next = pos;
}
}
void SLTErase(SL** PPhead, SL* pos)
{
if (pos == *PPhead)
{
SLTPopFront(PPhead);
}
else
{
SL* prev = *PPhead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}