数据结构--单链表的实现(c语言实现)

 

目录

概念

链表的概念及结构

一、实现的功能

二、依次实现

1.打印链表

2.尾插结点

3.尾删结点

4.头插结点

5.头删结点

6.查找结点

7.任意位置后一位的插入

8.任意位置后一位的删除

总结


概念

链表的概念及结构

概念:链表是一种物理存储结构上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针连接次序实现的。

现实中的链表:

数据结构中:

思考思考,实际中的链表会有非常多种

1.单向,双向

2.带头,不带头

3.循环,非循环

这样一列举就已经能组合出8种了qwq。


 

一、实现的功能

我们实现的是最基本的增删改查功能,通过列举,依次有以下几个接口需要我们实现: 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
typedef int SListDataType;

typedef struct SListNode
{
    SListDataType data;
    struct SListNode *next;
} SListNode;
// 打印链表
void SListPrint(SListNode *phead);
// 尾插节点
void SListPushBack(SListNode **phead, SListDataType x);
// 获得一个新节点
SListNode *GetNewSListNode();
// 尾删节点
void SListPopBack(SListNode **pphead);
// 头插结点
void SListPushFront(SListNode **phead, SListDataType x);
// 头删结点
void SListPopFront(SListNode **phead);
//查找结点
SListNode *SListFind(SListNode *phead, SListDataType x);
//任意位置插入结点
void SListInsertAfter(SListNode *pos, SListDataType x);
//任意位置删除结点
void SListEraseAfter(SListNode *pos);

二、依次实现

1.打印链表

代码如下:

void SListPrint(SListNode *pphead)
{
    SListNode *cur = pphead;
    while (cur)
    {
        printf("%d->", cur->data);//遍历每个结点并且打印data
        cur = cur->next;
    }
    printf("NULL\n");
}

2.尾插结点

2.尾插结点首先把头结点传入函数,接着通过指针遍历到最后一个结点,这之后向内存申请一个新结点,将这个新结点连接上

代码如下:

SListNode *GetNewSListNode()
{
    // 申请一个结点
    SListNode *NewNode = (SListNode *)malloc(sizeof(SListNode));
    // 申请失败,直接退出,简单粗暴
    if (NewNode == NULL)
    {
        printf("%s", strerror(errno));
        exit(-1);
    }
    return NewNode;
}
void SListPushBack(SListNode **pphead, SListDataType x)
{
    if (*pphead == NULL)
    {
        *pphead = GetNewSListNode();
        (*pphead)->data = x;
        (*pphead)->next = NULL;
    }
    else
    {
        SListNode *NewNode = GetNewSListNode();
        SListNode *tail = *pphead;
        //循环找到一个节点,这个节点中的节点指针是NULL
        //循环找到NULL
        while (tail->next != NULL)
        {
            tail = tail->next;
        }
        NewNode->data = x;
        NewNode->next = NULL;
        tail->next = NewNode;
    }
}

 

3.尾删结点

尾插结点就是把最后一个结点删除掉之后,要把新成为的最后一个结点中的next指针置NULL,

当然,我们要分三种情况讨论,第一种就是上面说的,第二种则是只要一个结点的时候,我们要把这个结点free掉,返回空指针,第三种则是没有结点的情况,直接返回空指针。

代码如下:

void SListPopBack(SListNode **pphead)
{
    // 1.空
    // 2.只有一个结点
    // 3.一个以上结点
    if (*pphead == NULL)
    {
        return;
    }
    else if ((*pphead)->next == NULL)
    {
        free(*pphead);
        *pphead = NULL;
    }
    else
    {
        SListNode *tail = *pphead;
        SListNode *pre = tail; //记录最后一个结点的前一个结点
        //通过循环找到最后一个结点
        while (tail->next != NULL)
        {
            pre = tail;
            tail = tail->next;
        }
        //释放掉tail
        free(tail);
        tail = NULL;
        // 之前倒数第二个结点成为了最后一个结点,置NULL
        pre->next = NULL;
    }
}

 

4.头插结点

头插结点首先要申请一个新结点,让这个结点的next指向原来的头,这之后在把头移到这个新结点上

 

代码实现:

void SListPushFront(SListNode **pphead, SListDataType x)
{
    SListNode *NewNode = GetNewSListNode();
    NewNode->data = x;
    // 新插入结点中存放的地址为之前第一个结点的地址
    NewNode->next = *pphead;
    //把NewNode作为第一个结点
    *pphead = NewNode;
}

 

5.头删结点

头删结点分两种情况,有结点和无结点,无结点时直接返回就好了,有结点的时候先让头指针指到下一个结点,之后再free掉前面那个结点

 

代码实现:

void SListPopFront(SListNode **pphead)
{
    // 1.没有结点
    // 2.有1个结点+ 3.有1个以上结点
    if (*pphead == NULL)
    {
        //没有要删的
        return;
    }
    else
    {
        // 1.保存头指针指向的下一个结点地址
        // 2.free掉头指针
        // 3.将头指针与下一个地址重新连接
        SListNode *next = (*pphead)->next;
        free(*pphead);
        *pphead = next;
    }
}

 

6.查找结点

没什么难的,就是遍历,遍历到了就返回,不到就返回NULL。

SListNode *SListFind(SListNode *phead, SListDataType x)
{
    SListNode *cur = phead;
    while (cur != NULL)
    {
        if (cur->data == x)
        {
            return cur;
        }
        cur = cur->next;
    }
    return NULL;
}

 

7.任意位置后一位的插入

传入位置后,首先要申请一个新结点,让结点指向位置的后一位,再让传入的位置指向新结点

代码实现:

void SListInsertAfter(SListNode *pos, SListDataType x)
{
    assert(pos);
    SListNode *NewNode = GetNewSListNode();
    NewNode->data = x;
    NewNode->next = pos->next;
    pos->next = NewNode;
}

8.任意位置后一位的删除

首先把要删除的结点临时保存,再把传入位置的结点的next指针再往后指一位,最后free掉之前保存的结点。

代码实现:

void SListEraseAfter(SListNode *pos)
{
    assert(pos);
    if (pos->next)
    {
        SListNode *next = pos->next;
        SListNode *nextnext = pos->next->next;
        pos->next = nextnext;
        free(next);
    }
}

总结

由于做链表的时候一直要对内存进行操作,一出错容易导致整个程序崩溃,所以每完成一个函数加一次测试,确保没问题。要非常熟悉各种插入删除的步骤,做到烂熟于心,还有在写代码之前,不妨先分析有几种情况,比如之前的尾删就有3种情况,要是一开始没考虑清楚,后面程序出错了再来修改反而需要更长的时间。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值