目录
一、链表介绍
二、单链表的实现
2.1 应用环境
2.2 单链表的接口实现——增删查改
(1)打印
(2)创造新的节点
(3)头插
(4)头删
(5)尾插
(6)尾删
(7)销毁
(8)查找
(9)在pos之前插入
(10)在pos后面插入
(11)删除pos位置
(12)删除pos后面位置
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表 中的指针链接次序实现的 。(下图为链式结构的手画简图)
但是有三个注意点:
1.链式结构在逻辑上是连续的,但在物理上不一定连续
2.现实中的结点一般从堆上申请出来
3.从对上申请的空间,是按照一定的策略来分配的,两次申请的空间,可能连续,也可能不连续
无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结 构的子结构,如哈希桶、图的邻接表等等。
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;//这里不能漏写struct,因为此处还没有缩写完成
}SLTNode;
打印的过程其实就是遍历的过程,一个一个访问。
void SListPrint(SLTNode* phead)
{
//phead是空的很正常,所以此处不用断言
SLTNode* cur = phead;//将phead赋给cur,是怕后期在遍历过程中找不到头
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL");
}
Q:为什么要单独写一段代码创造节点?
A: 在局部定义的话,出了作用域就会自动销毁
SLTNode* BuySLTNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail");
return -1;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SListPushFront(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = BuySLTNode(x);
newnode->next = *pphead;
*pphead = newnode;//这样phead就存了newnode的地址,跑前面去了
}
void SListPopFront(SLTNode** pphead)
{
assert(pphead);
//温柔检查
if (*pphead == NULL)
{
return;
}
暴力检查
//assert(*pphead != NULL);//为空可以插入但不能删除
SLTNode* del = *pphead;
*pphead = (*pphead)->next;//加括号的原因是两个同优先级
free(del);//结点是一个一个的,所以每次释放都是一个一个的
del = NULL;
}
void SListPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* newnode = BuySLTNode(x);
// 情况1.链表本身为空
// 情况2.链表本身不为空
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
// 找尾
SLTNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
//先找到最后一位
tail->next = newnode;
}
}
void SListPopBack(SLTNode** pphead)
{
assert(pphead);
//1.一个节点
//2.多个节点
//温柔检查
if (*pphead == NULL)
{
return;
}
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
//方法1
找尾
//SLTNode* prev = NULL;
//SLTNode* tail = pphead;
//while (tail->next != NULL)
//{
// prev = tail;
// tail = tail->next;
//}
//prev->next = NULL;
//free(tail);
//tail = NULL;
//方法2
SLTNode* tail = *pphead;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
void SListDestory(SLTNode** pphead)
{
assert(pphead);
SLTNode* cur = *pphead;
while (cur)
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SListPushFront(pphead, x);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
//暴力检查,pos不在链表中,prev为空,还没有找到pos,说明pos传错了
assert(prev);
}
SLTNode* newnode = BuySLTNode(x);
prev->next = pos;
newnode->next = pos;
}
}
void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode = BuySLTNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SListErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
SListPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
// 检查pos不是链表中节点,参数传错了
assert(prev);
}
prev->next = pos->next;
free(pos);
//pos = NULL;
}
}
void SListEraseAfter(SLTNode* pos)
{
assert(pos);
if (pos->next == NULL)
{
return;
}
else
{
SLTNode* next = pos->next;
pos->next = next->next;
free(next);
}
}
:如果对您有帮助的话,不要忘记一键三连哦,撒花