C语言数据结构篇——单链表的创建,插入,节点删除和打印等操作

简介: C语言数据结构篇——单链表的创建,插入,节点删除和打印等操作

线性表的顺序存储称为顺序表,而链表就是线性表的链式存储,而链表相对于顺序表的一个特点就是可以实现存储空间的动态管理,另一个就是每个节点的地址可能是不连续的,所以可以提高空间利用率,并且每个节点都由数据域和指针域构成。如果每个节点中只有指向后继节点的指针,那这就是我今天要分享的内容——单链表。


链表有很多种写法,有很多人定义一个节点结构体,又有部分人喜欢像顺序表一样定义一个头结点还有各种各样的写法,而我比较喜欢定义一个头结点的来保存链表的信息——(在我看来,第一个数据节点前面有头结点,最后一个数据节点后面有空指针,就可以使头插入,尾插入更加简单明了)那简单理解也就到这里了,下面给大家写一下以便大家可以更加直观的理解单链表。


定义头结点和数据节点:我们创建链表首先就需要创建头结点和定义节点结构体,链表的大小等信息就保存在头结点中,需要时从头结点中获取即可,代码如下:


typedef struct header//头结点
{
    int length;//记录链表大小
    struct node* next;//指向第一个节点的指针
}head;//别名方便使用
typedef struct node//数据节点
{
    int date;//数据域
    struct node* next;//指向下一个结点(指针域)
}node;

这样一个头结点和数据节点就定义好了,下面的代码中就可以直接引用啦;


链表的创建:创建链表只需要创建一个头结点,链表每储存一个元素就分配一个内存单元,再讲储存单元的地址保存在上一个节点中就可以了,并不需要在创建时把所有空间都分配好;链表普遍让最后一个节点的指针指向NULL方便遍历,查找等操作,代码如下:


head* createlist()//建立头结点保存链表信息
{
    head* temp;
    temp=(head*)malloc(sizeof(head));//为头结点分配空间,分配失败temp就等于NULL;
    temp->length=0;//链表初始大小
    temp->next=NULL;//重点
    return temp;//返回创建好的头结点
}

4d429f65bdaa42b3a4b7a1434fa19da1.png


这样一个包含头结点的链表就创建好了(思维图如上).

链表数据节点的插入:链表中插入元素要比顺序表方便,不用像顺序表那样移动元素,所以效率更快。举个例子,如下图,要将2这个节点插入到1和3两个节点中间,只需要先断开1和3之间的连接,再让1指向2,2向3即可。

aca0e0abf431407c8b61c5b55f680db5.png

链表数据节点插入的基本思路就差不多了,当然,这只是举个例子。具体的加入代码如下(这里只展示功能为链表插入数据节点的封装函数,完整代码在文章末尾):


int listinsert(head* p,int pos,int val)//头结点,需要插入的位置的下标值,插入的元素
{
    if(p==NULL||pos<0||pos>p->length)
    {
        return -1;
    }
    node* temp=(node*)malloc(sizeof(node));//为要插入的节点分配内存
    temp->date=val;//填充数据域
    node* pcur=p->next;//定义指向第一个节点的指针(方便遍历链表)
    if(pos==0)//头插
    {
        p->next=temp;
        temp->next=pcur;//与第一个节点相连,可以确保最后一个节点指向NULL
    }
    else
    {
        for(int i=1;i<pos;i++)//遍历链表
        {
            pcur=pcur->next;//指针指向的地址右移,直到找到数据;
        }
        temp->next=pcur->next;
        pcur->next=temp;
    }
    p->length++;//记录链表信息
    return 0;
}

链表数据节点的删除:相对于数据节点的插入,数据节点删除的思路就比较简单,只需要找到要删除元素的位置,使它的上一个节点中的指针直接指向删除位置的下一个节点就可以了。代码如下:

void listdelete(head *p,int val)//删除数据域为val的节点
{
    node *temp=p->next;//定义指向第一个数据节点的指针
    while(temp->date!=val)//遍历temp直到找到我们要删除的元素位置并让temp指向它
    {
        temp=temp->next;
    }
    if(temp->date!=val)//判断是否找到我们要删除的元素
    {
        printf("listdelete():error");
        return;//链表不存在该元素,退出程序
    }
    temp=p->next;//temp重新指向第一个数据节点,待会用来指向要删除的数据节点的前一个位置
    node* pcur=NULL;//用于指向要输出的数据节点的位置
    if(temp->date==val)//如果删除的是第一个节点
    {
        p->next=temp->next;//头结点直接指向第二个数据节点,达到删除的效果
        p->length--;//记录链表信息
        free(temp);//释放内存
        return ;//退出程序
    }
    else//如果删除的不是第一个元素
    {
        for(int i=0;i<p->length;i++)
        {
            pcur=temp->next;//pcur指向temp的下一个位置
            if(pcur->date==val)//pcur指向到要删除的的元素数据节点位置
            {
                temp->next=pcur->next;
                //pcur的上一个数据节点指向pcur的下一个数据节点,实现数据节点删除
                p->length--;//记录链表信息
                free(pcur);//都懂
                return;//结束程序
            }
            temp=temp->next;//不断右移,遍历链表
        }
    }
}

链表中数据节点的删除就可以实现了,但会不会认为后面表里打印链表的时候很麻烦?代码重复率会很高,所以我们为了方便也可以实现一个遍历打印链表的函数,而链表最后的数据节点又指向NULL,那么链表的打印函数就可以简单的实现了,具体代码如下。

void print(head *p)//打印链表
{
    if(p==NULL)
    {
        printf("listprint():error");
    }
    node *temp=p->next;
    while(temp!=NULL)//遍历链表
    {
        printf("%d ",temp->date);
        temp=temp->next;
    }
    printf("\n");
}

遍历打印链表的函数就实现了,我将链表的创建,插入,删除和输出这几个操作用一段代码一起演示出来,大家应该能够看的更清晰,注释就没有这么明了了,还是建议大家先看前面的。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct header
{
    int length;//链表大小
    struct node* next;//指向节点
}head;
typedef struct node
{
    int date;//数据域
    struct node* next;//指向下一个结点
}node;
//注意结构体的本名,别名以及typedef的用法
head* createlist()//建立头结点
{
    head* temp;
    temp=(head*)malloc(sizeof(head));//为头结点分配空间,分配失败temp就等于NULL;
    temp->length=0;
    temp->next=NULL;//重点
    return temp;//返回创建好的头结点
}
int listinsert(head* p,int pos,int val)//头结点,需要插入的位置的下标值,插入的元素
{
    if(p==NULL||pos<0||pos>p->length)
    {
        return -1;
    }
    node* temp=(node*)malloc(sizeof(node));//分配内存
    temp->date=val;//填充数据域
    node* pcur=p->next;//定义指向第一个节点的指针(方便遍历链表)
    if(pos==0)//头插
    {
        p->next=temp;
        temp->next=pcur;//与第一个节点相连
    }
    else
    {
        for(int i=1;i<pos;i++)
        {
            pcur=pcur->next;//指针指向的地址右移,直到找到数据;
        }
        temp->next=pcur->next;
        pcur->next=temp;
    }
    p->length++;
    return 0;
}
void print(head *p)//打印链表
{
    if(p==NULL)
    {
        printf("listprint():error");
    }
    node *temp=p->next;
    while(temp!=NULL)//遍历链表
    {
        printf("%d ",temp->date);
        temp=temp->next;
    }
    printf("\n");
}
void listdelete(head *p,int val)//删除数据域为val的节点
{
    node *temp=p->next;
    while(temp->date!=val)
    {
        temp=temp->next;
    }
    if(temp->date!=val)
    {
        printf("listdelete():error");
        return;
    }
    temp=p->next;//当前节点
    node* pcur=NULL;
    if(temp->date==val)
    {
        p->next=temp->next;
        p->length--;
        free(temp);//释放内存
        return ;
    }
    else
    {
        for(int i=0;i<p->length;i++)
        {
            pcur=temp->next;
            if(pcur->date==val)
            {
                temp->next=pcur->next;
                p->length--;
                free(pcur);
                return;
            }
            temp=temp->next;
        }
    }
}
int main()
{
    head* p;
    p=createlist();
    if(p==NULL)
    {
        printf("listcreat():error");
    }
    int n;//赋值的数据个数
    printf("请输入创建的链表节点个数\n");
    scanf("%d",&n);
    int arr[n];
    printf("请依次填充链表的数据域\n");
    for(int i=0;i<n;i++)
    {
        scanf("%d",&arr[i]);
    }
    for(int i=n-1;i>=0;i--)//将数组的值赋值给链表
    {
        listinsert(p,0,arr[i]);
    }
    printf("输出当前链表\n");
    print(p);
    int pos,val;
    printf("请依次输入需要插入的位置和元素\n");
    scanf("%d%d",&pos,&val);
    int m=listinsert(p,pos,val);
    if(m==-1)
    {
        printf("listinsert():error\n");
    }
    printf("输出插入后的链表\n");
    print(p);
    printf("请输入要删除的数据\n");
    scanf("%d",&val);
    listdelete(p,val);
    printf("输出删除后的链表\n");
    print(p);
}

好了,随便写点数据运行一下就是下面这个效果啦 ,感谢阅读。


请输入创建的链表节点个数

5

请依次填充链表的数据域

1 2 3 4 5

输出当前链表

1 2 3 4 5

请依次输入需要插入的位置和元素

4 6

输出插入后的链表

1 2 3 4 6 5

请输入要删除的数据

6

输出删除后的链表

1 2 3 4 5


值得一提的是,链表初始赋值的时候因为我用的都是头插(即从第一个数据节点插入),所以我如果直接输入1,2,3进行赋值,初始链表就会变成3,2,1,所以我用了数组保存我要赋值的链表元素,再让数组倒序对链表赋值,这个方法我觉得还是比较繁琐,大家有更好的方法也可以提出来,c语言小白一名,文章中如果有什么错误请大家见谅,当然,如果能够指出来就更棒了,十分感谢。


相关文章
|
24天前
|
存储 编译器 C语言
【数据结构】C语言实现链队列(附完整运行代码)
【数据结构】C语言实现链队列(附完整运行代码)
35 0
|
24天前
|
存储 算法 程序员
【数据结构】C语言实现顺序表万字详解(附完整运行代码)
【数据结构】C语言实现顺序表万字详解(附完整运行代码)
38 0
|
26天前
|
存储 算法 索引
数据结构与算法:单链表
朋友们大家好,本节来到数据结构与算法的新内容:单链表 在上篇文章中,我们知道顺序表通常需要预分配一个固定大小的内存空间, 通常以二倍的大小进行增容,可能会造成空间的浪费,本篇文章我们介绍的链表可以解决这个问题
|
6天前
|
存储 算法
单链表——“数据结构与算法”
单链表——“数据结构与算法”
|
20天前
|
算法 C语言
【算法与数据结构】 C语言实现单链表队列详解2
【算法与数据结构】 C语言实现单链表队列详解
|
20天前
|
存储 算法 C语言
【算法与数据结构】 C语言实现单链表队列详解1
【算法与数据结构】 C语言实现单链表队列详解
|
20天前
|
缓存 算法 搜索推荐
【数据结构】链表(单链表与双链表实现+原理+源码)
【数据结构】链表(单链表与双链表实现+原理+源码)
|
30天前
|
存储 机器学习/深度学习 算法
C语言代码实现数据结构与算法
以上代码中,哈希表使用链表解决哈希冲突,每个链表节点包含一个键值对。hash函数用于计算键值对应的哈希值,insert函数用于向哈希表中插入一个键值对,若当前位置为空,则直接插入;否则,将新节点插入到链表末尾。search函数用于在哈希表中查找指定键值的值,若存在则返回其值,否则返回-1。
32 1
|
1月前
|
存储 C语言
C语言结构体操作
C语言结构体操作
14 0
|
15天前
|
消息中间件 存储 搜索推荐
深入理解栈和队列(二):队列
深入理解栈和队列(二):队列
29 0

热门文章

最新文章