单链表在物理空间上非线性的,相对于顺序表更加节省空间。
链表由一个一个的节点组成,每一个节点储存着数据和指向下一节点的指针,所以链表在逻辑上是线性的。
单链表的打印
cpp
void SLTPrint(SLTNode* phead)
{
SLTNode* pcur = phead;
while (pcur)
{
printf(" %d ->", pcur->data);
pcur = pcur->next;
}
printf(" NULL");
}
这里由于不需要对结构体进行修改所以传递一级指针
在单链表中插入数据
设置结点
cpp
SLTNode* SLTBuyNode(SLTDataType x)
{
SLTNode SList;
SLTNode* tmp = (SLTNode*)malloc(sizeof(SList));
if (tmp == NULL)
{
perror("SLTBuyNode");
exit(1);
}
tmp->data = x;
tmp->next = NULL;
return tmp;
}
在单链表尾部插入数据
cpp
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* tmp = SLTBuyNode(x);
if (*pphead == NULL)
{
*pphead = tmp;
}
else
{
SLTNode* ptr = *pphead;
while (ptr->next)
{
ptr = ptr->next;
}
ptr->next = tmp;
}
}
在单链表头部插入数据
cpp
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* tmp = SLTBuyNode(x);
tmp->next = *pphead;
*pphead = tmp;
}
在单链表指定位置之前插入数据
cpp
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pphead && *pphead&&pos);
SLTNode* tmp = SLTBuyNode(x);
if (*pphead == pos)
{
SLTPushFront(pphead, x);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev -> next;
}
prev->next = tmp;
tmp->next = pos;
}
}
在单链表指定位置之后插入数据
cpp
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* tmp1 = pos->next;
SLTNode* tmp2 = SLTBuyNode(x);
pos->next = tmp2;
tmp2->next = tmp1;
}
删除单链表的数据
删除单链表尾部的数据
cpp
void SLTPopBack(SLTNode** pphead)
{
assert(pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* prev = *pphead;
SLTNode* ptr = *pphead;
while (ptr->next)
{
prev = ptr;
ptr = ptr->next;
}
free(ptr);
ptr = NULL;
prev->next = NULL;
}
}
删除单链表头部的数据
cpp
void SLTPopFront(SLTNode** pphead)
{
assert(pphead&&*pphead);
SLTNode* tmp = NULL;
tmp = (*pphead)->next;
free(*pphead);
*pphead = tmp;
}
删除单链表指定位置的数据
cpp
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead && *pphead&&pos);
if (pos==*pphead)
{
SLTPopFront(pphead);
}
else
{
SLTNode* tmp = *pphead;
while (tmp->next != pos)
{
tmp = tmp->next;
}
tmp->next = pos->next;
free(pos);
pos = NULL;
}
}
删除单链表指定位置之后的数据
cpp
void SLTEraseAfter(SLTNode* pos)
{
assert(pos && pos->next);
SLTNode* tmp = pos->next;
pos->next = tmp->next;
free(tmp);
tmp = NULL;
}
查找单链表中的数据
cpp
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
assert(phead);
SLTNode* ptr = phead;
while (ptr)
{
if (ptr->data == x)
return ptr;
else
ptr = ptr->next;
}
return NULL;
}
单链表的销毁
cpp
void SListDestroy(SLTNode** pphead)
{
assert(pphead && *pphead);
SLTNode* ptr = *pphead;
SLTNode* tmp = *pphead;
while (ptr)
{
tmp = ptr->next;
free(ptr);
ptr = tmp;
}
*pphead = NULL;
}
链表的分类
链表的结构非常多样,以下情况总结起来就有八种链表结构

带头
链表中有哨兵位节点,该哨兵位节点就是头节点。上文实现的链表中phead不表示头节点,表示第一个有效的节点。

单向/双向

循环/不循环

前面实现的链表就是不带头不循环 的单向链表,简称单链表
还有一个经常使用的链表就是带头循环 双向链表,简称双向链表