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

目录

以下是我们需要实现的功能

以下是我们上述功能的具体实现

一、创建新的结点函数

二、打印链表中的全部元素

三、尾插元素

四、头插元素

五、删除头结点

六、删除尾结点

七、查找指定元素

八、在pos位置之前插入

九、在pos位置之后插入元素

十、摧毁链表

以下是我们写在Slist.c中的完整代码

以下是我们的测试代码,写在test.c文件中


以下是我们需要实现的功能

以下的代码我们放在Slist.h的头文件中。

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include<assert.h>
typedef int SLTDataType;
typedef struct SlistNode
{
    SLTDataType data;
    struct SlistNode*next;
}SLTNode;

void SListPrint(SLTNode *phead);//打印链表中的全部元素
void SListPushBack(SLTNode**phead ,SLTDataType x);//尾插元素
void SListPushFront(SLTNode**phead ,SLTDataType x);//头插元素
SLTNode * BuyListNode(SLTDataType x);//添加新的结点
void SListPopBack(SLTNode** phead);//删除尾结点
void SListPopFront(SLTNode** phead);//删除头结点
SLTNode * SListFind(SLTNode*phead,SLTDataType x);//查找结点的指针

//在pos位置之前插入
void SListInsert(SLTNode**phead,SLTNode*pos,SLTDataType x);
//在pos位置之后插入
void SListErase(SLTNode**phead,SLTNode*pos,SLTDataType x);

// 单链表的销毁
void SListDestory(SLTNode * plist);

以下是我们上述功能的具体实现

以下代码放在Slist.c文件中

一、创建新的结点函数

在包含了我们的Slist.h的头文件之后,我们可以先写我们的BuyListNode函数,此函数的功能就是使用malloc开辟一个新的结点,并将我们新的结点的数据置为我们传入的x,将我们的新节点的next值置为NULL,并最后将我们新创建的结点返回。

这个函数主要是为我们接下来的链表的增加元素提供便利,我们在接下来的链表的增加元素直接调用此函数就可以。

#include "Slist.h"
SLTNode * BuyListNode(SLTDataType x)
{
    SLTNode *newnode=(SLTNode*)malloc(sizeof(SLTNode));
    assert(newnode);
    newnode->data=x;
    newnode->next=NULL;
    return newnode;
}

二、打印链表中的全部元素

打印链表需要将我们链表的头结点的指针给传入。这里我们先使用assert函数断言phead函数,防止phead指针为空指针。接下来,我们使用一个cur的临时指针来从我们的phead,也就是头结点来遍历我们整张链表。由于我们每一个结点的next的指针都指向下一个结点的地址,且最后一个结点的next为NULL,所以我们只要使用cur=cur->next,就能共遍历我们的整张链表。

void SListPrint(SLTNode *phead)
{
    assert(phead);
    SLTNode *cur=phead;
    while(cur)
    {
        printf("->%d",*cur);
        cur=cur->next;
    }
    printf("\n");
    printf("已经成功打印链表中的全部元素\n");
}

三、尾插元素

首先我们创建一个新的结点来存储我们的新的元素。这里我们就是直接使用BuyListNode函数来创建我们的新结点。但是,当我们尾插元素的时候,我们需要判断当前的链表是否为空,因为如果链表为空的话,我们的头结点为NULL,需要单独考虑,所以我们的代码使用if-else结构分出了两种情况,如果是空链表,我们就把我们创建的结点直接赋给头结点。此处我们需要注意,形参的改变1是不会影响实参的,所以我们需要使用传址的方式来传递我们的指针。由于我们所需要传递的是一维指针,所以我们就需要二维指针来我们的一维指针传递。另外一种情况,我们需要用我们创建的cur指针遍历找到我们当前的尾结点,再将我们的新创建的结点连接在我们的尾结点后面。

void SListPushBack(SLTNode**phead ,SLTDataType x)//尾插
{
    SLTNode *newnode=BuyListNode(x);
    if(*phead==NULL)//第一个值的插入
    {
        *phead=newnode;
    }
    else
    {
        SLTNode *cur = *phead;
        while (cur->next)
        {
            cur = cur->next;
        }
        cur->next = newnode;
    }
    printf("已经成功在链表的表尾插入元素%d\n",x);
}

四、头插元素

同样,这里我们和尾插一样,需要改变链表元素的地方我们都需要使用二级指针来传递我们的一级指针。我们开创了一个新的newnode,将我们的newnode的next指向我们的头结点,再将我们的头结点赋值为我们新生成的newnode。头插元素就不用考虑链表为空,因为即使链表为空,我们的newnode就是头结点,其next指针指向NULL,符合我们链表的规则。

void SListPushFront(SLTNode**phead ,SLTDataType x)
{
    SLTNode*newnode=BuyListNode(x);
    newnode->next=*phead;
    newnode->data=x;
    *phead=newnode;
    printf("已经成功在链表的表头插入元素%d\n",x);
}

五、删除头结点

这里我们先断言。如果链表中没有元素,我们是无法删除的。然后我们使用一个临时指针来记录我们头结点的下一个结点,然后将我们的头结点的指针所指向的空间释放,再将我们的头指针赋值为我们的临时指针。

void SListPopFront(SLTNode** phead)
{
    assert(*phead);
    SLTNode *p=(*phead)->next;
    free(*phead);
    *phead=p;
    printf("已经成功删除头结点\n");
}

六、删除尾结点

如果我们当前只有一个元素,那我们就直接释放我们当前的结点就行。如果我们不是只有一个元素,我们需要找到尾结点的前一个结点,尾结点就是p->next=NULL,而我们尾结点的前一个结点就是p->next->next=NULL,然后将我们的尾结点删除即可

void SListPopBack(SLTNode** phead)//删除尾结点
{
    assert(*phead);
    SLTNode *p=(*phead);
    if(p->next==NULL)
    {
        free(p);
        *phead=NULL;
        printf("已经成功删除尾结点\n");
    }
    else
    {
        while(p->next->next!=NULL)
        {
            p=p->next;
        }
        free(p->next);
        p->next=NULL;
        printf("已经成功删除尾结点\n");
    }

}

七、查找指定元素

此处我们创造一个临时指针,(和打印链表的原理相同),遍历链表,找到我们的结点我们就返回我们找到的结点的地址(这个功能可以和其他的函数联动)。如果遍历完了还没有找到,那就是查找不到。

SLTNode * SListFind(SLTNode*phead,SLTDataType x)
{
    assert(phead);
    SLTNode *cur=phead;
    while(cur)
    {
        if(cur->data==x)
        {
//            printf("已经查找到了,地址为%p\n",cur);
            return cur;
        }
        cur=cur->next;
    }
    printf("查找不到\n");
}

八、在pos位置之前插入

如果我们需要在链表的第一个元素之前插入,我们就调用头插的函数。如果不是,我们就需要找到我们的pos位置之前的那个函数(查找pos位置我们可以调用我们查找pos位置的函数),然后将我们newnode的next指向pos,将我们pos位置之前的那个指针指向newnode。

//在pos位置之前插入
void SListInsert(SLTNode**phead,SLTNode*pos,SLTDataType x)
{
    assert(phead);
    assert(pos);
    if (*phead == pos)
    {
        SListPushFront(phead, x);
        printf("已经成功插入x元素\n");
    }
    else
    {
        SLTNode*newnode=BuyListNode(x);
        SLTNode *p=*phead;
        while(p->next!=pos)
        {
            p=p->next;
        }
        newnode->next=pos;
        p->next=newnode;
        newnode->data=x;
        printf("已经成功插入x元素\n");
    }
}

九、在pos位置之后插入元素

在pos位置之后插入我们只需要找到我们的pos位置,然后调整以下链表指针的指向即可。

//在pos位置之后插入
void SListErase(SLTNode**phead,SLTNode*pos,SLTDataType x)
{
    assert(phead);
    assert(pos);
    SLTNode*newnode=BuyListNode(x);
    SLTNode *p=*phead;
    while(p!=pos)
    {
        p=p->next;
    }
    newnode->next=p->next;
    newnode->data=x;
    p->next=newnode;
    printf("已经成功插入x元素\n");

}

十、摧毁链表

摧毁链表需要我们创建两个临时指针,先将我们的临时指针指向我们所需要释放的结点的位置,然后将我们的另一个指针指向第一个结点指针的下一个结点,然后释放我们的第一个结点。再将我们的第二个结点赋值给我们的第一个结点,以此往复,就可以释放链表中的全部空间。

void SListDestory(SLTNode * plist)
{
    assert(plist);
    SLTNode *p=plist;
    SLTNode *q;
    while(p->next!=NULL)
    {
        q=p;
        p=p->next;
        free(q);
    }
    free(p);
    printf("已成功销毁\n");
}

以下是我们写在Slist.c中的完整代码

#include "Slist.h"
SLTNode * BuyListNode(SLTDataType x)
{
    SLTNode *newnode=(SLTNode*)malloc(sizeof(SLTNode));
    assert(newnode);
    newnode->data=x;
    newnode->next=NULL;
    return newnode;
}
void SListPrint(SLTNode *phead)
{
    assert(phead);
    SLTNode *cur=phead;
    while(cur)
    {
        printf("->%d",*cur);
        cur=cur->next;
    }
    printf("\n");
    printf("已经成功打印链表中的全部元素\n");
}

void SListPushBack(SLTNode**phead ,SLTDataType x)//尾插
{
    SLTNode *newnode=BuyListNode(x);
    if(*phead==NULL)//第一个值的插入
    {
        *phead=newnode;
    }
    else
    {
        SLTNode *cur = *phead;
        while (cur->next)
        {
            cur = cur->next;
        }
        cur->next = newnode;
    }
    printf("已经成功在链表的表尾插入元素%d\n",x);
}

void SListPushFront(SLTNode**phead ,SLTDataType x)
{
    SLTNode*newnode=BuyListNode(x);
    newnode->next=*phead;
    newnode->data=x;
    *phead=newnode;
    printf("已经成功在链表的表头插入元素%d\n",x);
}

void SListPopFront(SLTNode** phead)
{
    assert(*phead);
    SLTNode *p=(*phead)->next;
    free(*phead);
    *phead=p;
    printf("已经成功删除头结点\n");
}

void SListPopBack(SLTNode** phead)//删除尾结点
{
    assert(*phead);
    SLTNode *p=(*phead);
    if(p->next==NULL)
    {
        free(p);
        *phead=NULL;
        printf("已经成功删除尾结点\n");
    }
    else
    {
        while(p->next->next!=NULL)
        {
            p=p->next;
        }
        free(p->next);
        p->next=NULL;
        printf("已经成功删除尾结点\n");
    }

}

SLTNode * SListFind(SLTNode*phead,SLTDataType x)
{
    assert(phead);
    SLTNode *cur=phead;
    while(cur)
    {
        if(cur->data==x)
        {
//            printf("已经查找到了,地址为%p\n",cur);
            return cur;
        }
        cur=cur->next;
    }
    printf("查找不到\n");
}

//在pos位置之前插入
void SListInsert(SLTNode**phead,SLTNode*pos,SLTDataType x)
{
    assert(phead);
    assert(pos);
    if (*phead == pos)
    {
        SListPushFront(phead, x);
        printf("已经成功插入x元素\n");
    }
    else
    {
        SLTNode*newnode=BuyListNode(x);
        SLTNode *p=*phead;
        while(p->next!=pos)
        {
            p=p->next;
        }
        newnode->next=pos;
        p->next=newnode;
        newnode->data=x;
        printf("已经成功插入x元素\n");
    }
}
//在pos位置之后插入
void SListErase(SLTNode**phead,SLTNode*pos,SLTDataType x)
{
    assert(phead);
    assert(pos);
    SLTNode*newnode=BuyListNode(x);
    SLTNode *p=*phead;
    while(p!=pos)
    {
        p=p->next;
    }
    newnode->next=p->next;
    newnode->data=x;
    p->next=newnode;
    printf("已经成功插入x元素\n");

}

void SListDestory(SLTNode * plist)
{
    assert(plist);
    SLTNode *p=plist;
    SLTNode *q;
    while(p->next!=NULL)
    {
        q=p;
        p=p->next;
        free(q);
    }
    free(p);
    printf("已成功销毁\n");
}

以下是我们的测试代码,写在test.c文件中

#include <stdio.h>
#include "Slist.h"

void TestSList1()
{

    SLTNode *n1=NULL;
    SListPushBack(&n1,13);
    SListPushBack(&n1,20);
    SListPushBack(&n1,80);
    SListPushBack(&n1,9);
    SListPushBack(&n1,12);
    SListPushBack(&n1,32);
    SListPushFront(&n1,4);
    SListPrint(n1);
    SListPopFront(&n1);
    SListPrint(n1);
    SListPopBack(&n1);//删除尾结点
    SListPrint(n1);

    SListInsert(&n1,SListFind(n1,13),10);
    SListPrint(n1);
    SListErase(&n1,SListFind(n1,9),19);
    SListPrint(n1);
    SListDestory(n1);
}
int main() {

    TestSList1();
    return 0;
}

 

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

桜キャンドル淵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值