单链表代码实现

完整代码:链接:https://pan.baidu.com/s/1-FDdb47ehW8Zw1ds0yPjMw?pwd=jyhh 
提取码:jyhh

1.单链表实现思路

单链表实现和顺序表差不多,创建一个结构体,利用结构体实现头插、尾插、头删、尾删、查找、任意位置插入或删除等的接口函数

2.单链表实现过程

2.1尾插

尾插实现基本思路是找到尾节点,将尾节点的next指针指向要插入的节点,如下图2-1所示,要注意的是要考虑链表为空的情况,所以第一步是先创建一个新节点,具体实现如下所示:

图2-1 头删示意图

void SListPushBack(SLTNode** pphead, SLDataType x)
{

    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;
    newnode->next = NULL;
    if (newnode == NULL)
    {
        printf("malloc fail\n");
        exit(-1);
    }
    if (*pphead == NULL)
    {
        *pphead = newnode;
    }
    else
    {
        SLTNode* tail = *pphead;
        while(tail->next != NULL)
        {
            tail = tail->next;
        }

        tail->next = newnode;
    }
    
}

2.2头插

头插就是先创建一个新节点,将新节点的next指针指向链表的头节点即可,具体实现如下所示:

图2-2 头插示意图

void SListPushFront(SLTNode** pphead, SLDataType x)
{
    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    newnode->data = x;
    newnode->next = NULL;
    if (newnode == NULL)
    {
        printf("malloc fail\n");
        exit(-1);
    }
    newnode->next = *pphead;
    *pphead = newnode;
}

2.3尾删

尾删实现同样要先找到尾节点,但这时我们还需要另外一个指针prev来记录倒数第二个节点,如下图2-3所示,以避免在删除尾节点的时候,出现野指针的情况;还要考虑的一点是如果链表只有一个节点,那么我们无法将该节点置为空,所以我们需要单独处理它,具体实现如下所示:

图2-3 尾删示意图

void SListPopBack(SLTNode** pphead)
{
    assert(*pphead != NULL);
    if ((*pphead)->next == NULL)
    {
        free(*pphead);
        *pphead = NULL;
    }
    else
    {
        SLTNode* tail = *pphead;
        SLTNode* prev = NULL;
        while (tail->next != NULL)
        {
            prev = tail;
            tail = tail->next;
        }
        free(tail);
        tail = NULL;
        prev->next= NULL;
    }
    

}

2.4头删

头删直接创建一个节点指向头结点的下一个节点,释放头节点再将新头节点的值赋给头节点,如下图2-4所示,注意此时链表不能为空,具体实现如下:

图2-4 头删示意图

void SListPopFront(SLTNode** pphead)
{
    assert(*pphead != NULL);
    SLTNode* head = (*pphead)->next;
    free(*pphead);
    (*pphead)= head;
}

2.5查找

查找就是遍历整个链表,如果找到了要找的值就返回该值对应的地址,如果没有找到,就返回空;另外查找还可以实现修改的功能,就是在查找到该值的时候利用返回的地址修改值,具体实现如下所示:

SLTNode* SListFind(SLTNode* phead, SLDataType x)
{
    SLTNode* cur = phead;
    while (cur)
    {
        if (cur->data == x)
        {
            return cur;
        }
        else
        {
            cur = cur->next;
        }
    }
    return NULL;
}

 修改值:

    SLTNode* pos = SListFind(plist, 2);
    if (pos)
    {
        SListInsert(&plist, pos, 20);
    }

2.6任意位置的插入

2.6.1在任意位置之前插入

pos位置前插入时需要设置两个变量,一个变量prev存pos前一个位置的地址,另一个变量cur存pos的地址,让prev的next指向要插入的新变量newnode的地址,再将newnode的next指向cur的地址就完成插入,思路实现如下图2-5所示;但要注意的是当pos节点为头结点时,prev的值无法设置,所以这种情况要单拎出来解决,详细实现如下所示:

图2-5 任意位置之前插入思路图

void SListInsert(SLTNode** pphead, SLTNode* pos, SLDataType x)
{
    assert(pphead);
    SLTNode* newnode = BuyListNode(x);
    if (pos == *pphead)
    {
        newnode->next = pos;
        *pphead = newnode;
    }
    else
    {
        //找到pos前一个位置
        SLTNode* PosPrev = *pphead;
        while (PosPrev->next != pos)
        {
            PosPrev = PosPrev->next;
        }
        PosPrev->next = newnode;
        newnode->next = pos;
    }
    
}

2.6.2在任意位置之后插入

在pos节点后插入与在之前插入相比就少了考虑头结点的情况,直接插入就行,具体实现如下:

void SListInsertAfter(SLTNode* pos, SLDataType x)
{
    SLTNode* newnode = BuyListNode(x);
    newnode->next = pos->next;
    pos->next = newnode;
}

2.7任意位置的删除

2.7.1任意位置的删除

任意位置的删除要创建一个变量prev存pos前一个位置的地址,将prev的next指向pos的next释放pos当前节点即可,思路实现如下图2-6所示;这里还要注意删除头结点的情况,就直接将头节点删除,赋予新的头节点即可,具体实现如下所示:

图2-6 任意位置删除思路图

void SListErase(SLTNode** pphead, SLTNode* pos)
{
    assert(pphead);
    if (*pphead == pos)
    {
        *pphead = pos->next;
        free(pos);
    }
    else
    {
        SLTNode* prev = *pphead;
        while(prev->next != pos)
        {
            prev = prev->next;
        }
        prev->next = pos->next;
        free(pos);
    }
}

2.7.2删除任意位置之后的节点

删除pos位置之后的节点,就不用考虑头结点的问题,直接将pos的next指向变量next的next,最后将next释放即可,思路图如下图2-7所示,代码具体实现如下所示:

图2-7 删除pos后一个结点的思路图

 void SListEraseAfter(SLTNode** pphead, SLTNode* pos)
{
    assert(pphead);
    SLTNode* next = pos->next;
    pos->next = next->next;
    free(next);
}

2.8销毁单链表 

销毁单链表要先将下一个节点的地址给保留,释放当前节点即可,具体实现如下所示:

void SListDestroy(SLTNode** pphead)
{
    assert(pphead);
    SLTNode* cur = *pphead;
    SLTNode* next = cur->next;
    while (cur)
    {
        next = cur->next;
        free(cur);
        cur = next;
    }
    *pphead = NULL;
}

3.总结 

本次单链表的代码实现过程中,我深刻体会到了assert的精髓,因为我传参的时候传错了,找问题一直找不出来,调试了好久最后发现本来要传的是&plist,结果传成了plist,这个如果要是加一个断言,直接能报错,assert真的很有用!

代码实现方面就是一定要考虑全面,头尾结点时容易忽略的情况要注意,最重要的是要谨记单链表每一个节点里边都有下一个结点的地址,所以处理的时候一定要先把下一个结点的地址给保存好再进行操作~~~~~

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值