一、单向链表的结构
链表的物理结构
二、C语言实现链表
写代码的IDE是vs2019
1、使用结构体定义一个节点的原型,包括存储数据的data和指向下一个节点的指针next。
//链表的结构体原型
typedef int SLDatatype;
typedef struct SListNode
{
SLDatatype data;//数据
struct SListNode* next;//下一个节点的地址
}SLTNode;
2、利用next遍历整个链表,直到遇到NULL,打印链表数据
//链表打印
void SLTPrint(SLTNode* phead)
{
//空链表也可以打印,也没有必须要断言
SLTNode* cur = phead;//链表的头指针
while (cur)//当地址不为空
{
printf("%d->", cur->data);//打印节点里面的数据
cur = cur->next;//把下一个节点的地址赋值到当前地址
}
printf("NULL\n");
}
3、开辟新节点,插入链表的头
//链表头插
void SLPushFront(SLTNode** phead, SLDatatype x)
{
//assert(phead);
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//创建一个新节点
if (newnode == NULL)
{
perror("malloc fail:");
return;
}
newnode->data = x;//新节点赋新值
newnode->next = NULL;//新节点初始化next地址
newnode->next = *phead;//新节点的next赋值上一个链表的指针
*phead = newnode;//新节点的指针赋值成为链表的指针
}
```c
//创建节点并赋值和置空
SLTNode* BuyLTNode(SLDatatype x )
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL)
{
perror("malloc fail:");
return;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
//链表头插 简化版
void SLPushFront(SLTNode** pphead, SLDatatype x)
{
assert(pphead);//这里需要断言,因为如果这里是空的话,代表没有链表,它是头指针plist的地址
//assert(*pphead)//这里不需要断言,因为链表就算是空的也可以头插
SLTNode* newnode = BuyLTNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
4、开辟新节点
//链表尾插
void SLPushBack(SLTNode** pphead, SLDatatype x)
{
assert(pphead);//plist头指针不能为空,否则就是链表都不存在
//assert(*pphead);链表为空,可以尾插,不需要断言
//1、空链表
//2、非空链表
if (*pphead == NULL)
{
*pphead = BuyLTNode(x);
}
else
{
SLTNode* tail = *pphead;
//SLTNode* tmp = NULL;
/*while (tail)
{
tmp = tail;
tail = tail->next;
}
tmp->next = BuyLTNode(x);*/
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = BuyLTNode(x);
}
}
5、释放头结点,并且连起下一个节点
//头结点删除
void SLPopFront(SLTNode** pphead)
{
//空链表
assert(pphead);//这里需要断言,因为这里为空就是代表头指针plist的地址都有
assert(*pphead);//这里需要断言,因为这里为空代表链表一个节点都没有,你怎么头删呢
//非空
SLTNode* tmp = *pphead;
*pphead = (*pphead)->next;
free(tmp);
}
6、释放尾节点,并且next置空
//尾节点删除
void SLPopBack(SLTNode** pphead)
{
assert(pphead);
assert(*pphead);//链表为空
//只有一个节点
if ((*pphead)->next==NULL)
{
free(*pphead);
*pphead = NULL;
}
// 多个节点
else
{
SLTNode* tail = *pphead;
SLTNode* prev = NULL;
while (tail->next)
{
prev = tail;
tail = tail->next;
}
free(tail);
prev->next = NULL;
//写法二
/*while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;*/
}
}
7、遍历查找,返回找到的地址
//单链表查找
SLTNode* STFind(SLTNode* phead, SLDatatype x)
{
//assert(phead);//没必要断言,空链表查找了一下,查不到也没有问题, 就是找不到啊
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
8、中间插入节点
//在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLDatatype x)
{
assert(pphead);//链表的头指针要断言
assert(pos);//pos是你要插入的数字的节点的地址,如果是空就是尾插了,这样也可以不断言
//assert(*pphead)//*pphead断言就是空链表,空链表插入那就是pos为NULL,所以没有必要断言*pphead
if (*pphead == pos)
{
SLPopFront(pphead, x);
}
else
{
SLTNode* newnode = BuyLTNode(x);
SLTNode* cur = *pphead;
SLTNode* end = NULL;
while (cur)
{
if (cur == pos)
{
newnode->next = pos;
end->next = newnode;
break;
}
end = cur;
cur = cur->next;
}
}
}
//在pos之后插入
void SLInsertAfter(SLTNode** pphead, SLTNode* pos, SLDatatype x)
{
assert(pos);
SLTNode* newnode = BuyLTNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
9、中间删除节点
//删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead);
assert(*pphead);
if (*pphead == pos)
{
SLPopFront(pphead);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}
//删除pos后一位节点
void SLEraseAfter(SLTNode* pos)
{
assert(pos);//不能删除空指针的后一位
assert(pos->next);//最后一个节点的后一位就没有了,所以要断言
SLTNode* next = pos->next;
pos->next = next->next;
free(next);
/*pos->next = (pos->next)->next;
free(pos->next);*/
}
10、释放链表
//链表的释放
void SLDestory(SLTNode** pphead)
{
assert(pphead);
SLTNode* cur = *pphead;
/*while (cur)
{
SLTNode* del = cur;
cur = cur->next;
free(del);
}*/
while (cur)
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;//plist置空,如果函数用一级指针这里不能置空,需要手动在外部把plist置空
}