文章目录
链表的实现
注意:
-
在主函数中先创建一个结构体指针 plist 指向NULL,用来往后链表的一系列操作。
-
一共有三个文件test.c , SList.h , SList.c
-
如果想要修改plist需要传plist的地址,也就是二级指针。
代码如下
SList.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDataType;
typedef struct SListNode
{
SLTDataType val;
struct SListNode* next;
}SLTNode;
void SLTPrint(SLTNode* phead);
//头部插入删除/尾部插入删除
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SListDesTroy(SLTNode** pphead);
SList.c
申请节点
SLTNode* SLTBuyNode(SLTDataType x)
{
SLTNode* tmp = (SLTNode*)malloc(sizeof(SLTNode));
if (tmp == NULL)
{
perror("malloc:");
return 0;
}
tmp->next = NULL;
tmp->val = x;
return tmp;
}
尾部插入
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
//链表为空
if (*pphead == NULL)
{
*pphead = SLTBuyNode(x);
}
//链表不为空
else
{
SLTNode* cur = *pphead;
while (cur->next)
{
cur = cur->next;
}
//此时cur是尾节点
cur->next = SLTBuyNode(x);
}
}
情况1:链表为空
直接让 plist 指向新创建的节点
情况2:链表不为空
先用循环找到尾节点,找到后让cur->next = 新的节点。
头部插入
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
assert(pphead);
SLTNode* cur = SLTBuyNode(x);
cur->next = *pphead;
*pphead = cur;
}
先定义一个指针 cur 让 cur 指向 newnode ,然后让新节点的 next 指向 *pphead ,再让 * phead指向cur。
尾部删除
void SLTPopBack(SLTNode** pphead)
{
//链表不能为空
assert(pphead && *pphead)
//链表只有一个节点
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
SLTNode* prev = *pphead;
SLTNode* ptail = *pphead;
//链表有多个节点,可以用prev->next->next,也可以用prev 和ptail
while (ptail->next)
{
prev = ptail;
ptail = ptail->next;
}
free(ptail);
ptail = NULL;
prev->next = NULL;
}
}
情况1:链表只有一个节点
直接释放*pphead,然后置空。
情况2:链表有多个节点
只用尾指针 ptail 的话没有办法把上一个节点的next置空,虽然可以用prev->next->next来做,但是用双指针更加便于理解。
我们先定义两个指针 prev 和 ptail ,然后找到尾的同时也找到了尾的上一个,然后释放 patil ,让 ptail 和 prev->next 置空。
头部删除
void SLTPopFront(SLTNode** pphead)
{
assert(pphead && *pphead)
SLTNode* tmp = NULL;
//SLTNode* cur = *pphead;
//tmp = cur->next;
//free(cur);
//cur = tmp;
//如果要修改plist 的值就不能用cur来修改,只能通过指向plist的指针(pphead)解引用才能改变plist的值
tmp = (*pphead)->next;
free(*pphead);
*pphead = tmp;
}
注意的是头删会修改 plist 的值所以一定要用 *pphead,才能修改 plist 的值。
查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->val == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
查找不许要修改头 plist 所以传的是一级指针,让指针cur遍历链表,找到了就返回当前地址,找不到就返回NULL。
在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
assert(pphead && *pphead);
assert(pos);
if (pos == *pphead)
{
SLTPushFront(pphead, x);
}
else
{
SLTNode* cur = *pphead;
while ((cur->next) != pos)
{
cur = cur->next;
}
cur->next = SLTBuyNode(x);
cur->next->next = pos;
}
}
情况1:在第一个节点前插入
判断一下是不是在头节点前插入数据,如果是那么就直接调用头插。
情况2:非头插
先创建 cur 让cur找到 pos 的前一个节点,然后让cur指向新的节点,在让新的节点的next(cur->next->next)指向 pos,完成插入。
在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
assert(pos);
SLTNode* newnode = SLTBuyNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
创建一个新的节点,让新的节点的 next 指向 pos 的 next ,再让 pos 的 next 指向新的节点。
删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
assert(pphead && *pphead);
if (pos == *pphead)
{
SLTPopFront(pphead);
return;
}
if (pos->next == NULL)
{
SLTPopBack(pphead);
return;
}
SLTNode* cur = *pphead;
while ((cur->next) != pos)
{
cur = cur->next;
}
SLTNode* tmp = pos->next;
free(pos);
cur->next = tmp;
}
情况1:头删,和尾删就直接调用
情况2:非头删尾删
先定义指针 cur 找到要删除位置pos 的前一个节点,再用 tmp 存储 pos 下一个节点,释放pos后让cur的next指向tmp。
删除pos之后的节点
void SLTEraseAfter(SLTNode* pos)
{
assert(pos && pos->next);
SLTNode* del = pos->next;
pos->next = del->next;
free(del);
del = NULL;
}
要删除的为空就会报错
销毁链表
void SListDesTroy(SLTNode** pphead)
{
assert(pphead && *pphead);
SLTNode* pcur = *pphead;
while (pcur)
{
SLTNode* next = pcur->next;
free(pcur);
pcur = next;
}
*pphead = NULL;
}
pphead指针和链表都不能为空。定义一个指针依次遍历链表来释放。
test.c
#include"SList.h"
void test1()
{
SLTNode* plist = NULL; //重要
SLTPushBack(&plist,1);
SLTPushBack(&plist,2);
SLTPushBack(&plist,3);
SLTPrint(plist);
SListDesTroy(&plist);
}
int main()
{
test1();
return 0;
}