概念与结构
概念:
链表是一种物理储存结构 上非连续,非顺序的储存结构,数据元素的逻辑顺序 是通过链表中的指针链接次序实现的。
链表结构:

结点
与顺序表不同的是,链表里的每节"车厢"都是独立申请下来的空间,我们称之为"结点"。

- 结点的组成主要有两个部分:当前结点要保存的数据和保存下一个结点的地址。
- 图中指针变量plist保存的是第一个结点的地址,我们称*plist此时指向第一个结点,*如果我们希望plist指向第二个节点时,只需要修改plist保存的地址为0x00000B.
- 链表中每个结点都是独立申请的(即需要插入数据时才去申请一块结点的空间),我们需要通过指针变量来保存下一个结点位置才能从当前结点找到下一个结点。
链表的性质
- 链式机制在逻辑上是连续的,在物理结构上不一定连续。
- 结点一般是从堆上申请的。
- 从堆上申请来的空间,是按照一定策略分配出来的,每次申请的空间可能连续,可能不连续。
假设我们当前保存的结点为整型:
cpp
struct SListNode
{
int data;//结点数据
struct SListNode* next;//指针变量:用来保存下一个结点的地址
};
当我们想要保存一个整型数据时,实际是向操作系统申请了一块内存,这个内存不仅要保存整型数据,也需要保存下一个结点的地址。
链表的打印

cpp
typedef int SLDateType;
typedef struct SListNode
{
SLDateType data;
struct SListNode* next;
}SLTNode;
void SLPrint(SLTNode* phead)
{
SLTNode* pcur = phead;
while (pcur)
{
printf("%d", pcur->data);
pcur = pcur->next;
}
printf("\n");
}
当我们想要保存的数据类型为浮点型,字符型时,我们也可以改变。
链表的实现
接下来我们来实现这些函数
cpp
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
//需要找到pos之前的位置
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos);
// 在pos的前面插入
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x);
// 删除pos位置
//需要找到pos之前的位置
void SLTErase(SListNode** pphead, SListNode* pos);
void SLTDestroy(SListNode** pphead);
cpp
//向操作系统申请一个新空间
SListNode* BuySListNode(SLTDateType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
if (newnode == NULL)
{
perror("malloc fail!");
exit(1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
cpp
void SListPrint(SListNode* plist)
{
SListNode* pcur = plist;
while (pcur)
{
printf("%d ", pcur->data);
pcur = pcur->next;
}
printf("\n");
}
cpp
void SListPushBack(SListNode** pplist, SLTDateType x)
{
assert(pplist);
SListNode* newnode = BuySListNode(x);
//链表为空
if (*pplist == NULL)
{
*pplist=newnode;
}
//链表不为空
else
{
SListNode* ptail = *pplist;
while (ptail->next!=NULL)
{
ptail = ptail->next;
}
ptail->next = newnode;
}
}
cpp
void SListPushFront(SListNode** pplist, SLTDateType x)
{
assert(pplist);
SListNode* newnode = BuySListNode(x);
newnode->next = *pplist;
*pplist = newnode;
}
cpp
void SListPopBack(SListNode** pplist)
{
assert(pplist && *pplist);
//只有一个结点
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* prev=NULL, * ptail=*pplist;
while (ptail->next)
{
prev = ptail;
ptail = ptail->next;
}
free(ptail);
ptail = NULL;
prev->next = NULL;
}
}
cpp
void SListPopFront(SListNode** pplist)
{
assert(pplist && *pplist);
SListNode* pcur = *pplist;
*pplist = pcur->next;
free(pcur);
}
cpp
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
SListNode* pos = plist;
while(pos)
{
if (pos->data == x)
{
return pos;
}
pos = pos->data;
}
return NULL;
}
cpp
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
assert(pos);
SListNode* newnode = BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
cpp
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x)
{
assert(pphead && *pphead && pos);
SListNode* prev = *pphead;
//pos为第一个结点时
if (pos == *pphead)
SListPushFront(pphead, x);
else
{
SListNode* newnode = BuySListNode(x);
while (prev->next!= pos)
{
prev = prev->next;
}
newnode->next = pos;
prev->next = newnode;
}
}
cpp
void SListEraseAfter(SListNode* pos)
{
assert(pos && pos->next);
SListNode* pcur = pos->next;
pos->next = pcur->next;
free(pcur);
pcur = NULL;
}
cpp
void SLTErase(SListNode** pphead, SListNode* pos)
{
assert(pphead && *pphead && pos);
if (pos == *pphead)
{
SListPopFront(pphead);
}
else
{
SListNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
void SLTDestroy(SListNode** pphead)
{
assert(pphead);
SListNode* pcur = *pphead;
SListNode* prev = pcur;
while (pcur)
{
pcur = pcur->next;
free(prev);
prev = pcur;
}
*pphead = NULL;
}