一、单向链表的结构

链表的物理结构
image-20231221222113929.png

二、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;//新节点的指针赋值成为链表的指针
}

//创建节点并赋值和置空
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置空

}


Hhh_灏
24 声望3 粉丝