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

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

双链表的理解


·一般学习双链表都是在学习单链表之后(本文需要一定单链表基础),但单链表有一个缺点,就是无法访问前驱节点,需要查找某个已知节点的前一个节点时能再次从头遍历,就比较麻烦,那么,在节点中再加一个指针指向前驱节点的链表就称为双链表,再综合单链表的节点写法,那么双链表的写法就很简单了。数据节点包含一个数据域,两个指针(一个指向前驱节点,一个指向后驱节点)。大致图解如下:


a6faf2b095fe4b7585fcc2f36ca8b3f0.png

而因为多了一个前指针,所以数据节点的插入,删除等操作相对于单链表来说更难一点,但理解透了其实差不多,这里都不给出具体图解了,我们直接进入正文。


双链表数据节点和头结点的定义


双链表的数据节点定义只需要在定义单链表数据节点的基础上加一个前驱指针,这里我们还是定义一个头结点结构体来保存双链表的信息,另一个结构体充当数据节点  


typedef struct header//头结点保存双链表信息
{
    int length;//记录双链表当前大小
    struct node* next;//指向第一个数据节点
}head;
typedef struct node//数据节点
{
    int val;//数据域
    struct node* pre;//指向前一个数据节点(与单链表最大的区别)
    struct node* next;//指向下一个数据节点
}node;

如上,一个最基础的双链表头结点和数据节点就定义好了,下面就可以对双链表进行操作啦。


双链表的创建


和单链表的创建一样,双链表的创建同样只需要创建好头结点就可以了,链表每储存一个元素就分配一个内存单元构建数据节点 ,所以双链表的创建也相对简单,具体代码如下:


head* listcreat()//创建双链表
{
    head* p;
    p=(head*)malloc(sizeof(head));//给头结点分配空间
    p->length=0;//初始化双链表
    p->next=NULL;//双链表的最后指向NULL即可
    return p;//返回创建好的头结点地址
}


双链表数据节点的插入


学习双链表在我看来比较难的便是双链表数据节点的插入,主要是不好想, 所以建议大家可以在本子上画一下,具体代码如下(本文都是用封装函数的形式写的,文章末尾有完整代码):


void listinsert(head* p,int pos,int x)//头结点地址,要插入的位置,要插入的元素
{
    node* temp;//temp即我们要插入的数据节点
    temp=(node*)malloc(sizeof(node));//为其分配空间
    temp->val=x;//填充数据域
    if(p==NULL||pos<0||pos>p->length)//判断插入失败的情况
    {
        printf("listinsert():error\n");
        return;
    }
    if(p->length==0)//判断双链表为空的情况
    {
        p->next=temp;//头结点的的next指针指向temp
        temp->next=NULL;//temp的next指针指向NULL(保证最后一个数据节点的next指向空)
        temp->pre=NULL;temp的pre指针指向NULL(保证第一个数据节点的pre指向空)
        p->length++;//不要忘记记录双链表的大小
        return;//完成插入
    }
    node* pcur=p->next;//定义一个指向第一个数据节点的指针;
    if(pos==0)//插在头结点和第一个数据节点之间(即头插)的情况
    {
        p->next=temp;//头结点的next指针指向temp
        temp->pre=NULL;//temp此时为第一个数据节点所以前指针pre指向空
        temp->next=pcur;//pcur为上文记录的插入前的第一个数据节点
        pcur->pre=temp;//作为第二个数据节点的前指针pre指向第一个数据节点temp
        p->length++;
        return;
    }
    for(int i=1;i<pos;i++)
    {
        pcur=pcur->next;
    }
   //此时pcur指向要插入的位置的前一个数据节点,例如将2插入到1,3中间,那pcur就指向1.
    if(pos==p->length)//尾插的情况
    {
        pcur->next=temp;
        temp->next=NULL;
        temp->pre=pcur;
    }
    else//既不是头插,也不是尾插(可能比较难理解,建议画个草图)
    {
        temp->next=pcur->next;
        pcur->next->pre=temp;
        temp->pre=pcur;
        pcur->next=temp;
    }
    p->length++;
    return;
}


双链表数据节点的删除


双链表的数据节点插入弄明白之后数据节点的删除就简单的多啦,直接附上代码:


void listdelete(head* p,int x)//头结点地址,要删除的元素
{
    node* temp=p->next;//定义一个指向第一个数据节点的指针
    while(temp->val!=x&&temp!=NULL)//遍历链表寻找要删除的数据节点
    {
        temp=temp->next;
    }
    if(temp->val!=x)//链表中没有找到要删除的数据节点
    {
        printf("listdelete():error");
        return;//退出程序
    }
    node* pRe=temp->pre;//指向要删除的元素的前一个数据节点
    node* pnext=temp->next;//指向要删除的元素的后一个节点
    if(pRe==NULL)//即要删除的数据节点是第一个数据节点的情况
    {
        p->next=pnext;//头结点的next指针直接指向第二个数据节点
        pnext->pre=NULL;//第二个数据节点变成第一个数据节点
    }
    else if(pnext==NULL)//要删除的数据节点是最后一个的情况
    {
        pRe->next=NULL;
    }
    else//要删除的数据节点不是第一个也不是最后一个
    {
        pRe->next=pnext;
        pnext->pre=pRe;
    }
    free(temp);
    p->length--;
    return;
}


双链表的数据节点插入我们也完成了,为了方便使用,我们也可以写一个遍历打印(即输出)双链表的函数,具体代码如下:


void print(head* p)
{
    if(p==NULL)
    {
        printf("listprint():error\n");
        return;
    }
    node* temp=p->next;
    while(temp!=NULL)
    {
        printf("%d ",temp->val);
        temp=temp->next;
    }
    printf("\n");
    return;
}


好了,双链表的各个操作差不多就弄明白了,完整的代码如下:


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct header
{
    int length;
    struct node* next;
}head;
typedef struct node
{
    int val;
    struct node* pre;
    struct node* next;
}node;
head* listcreat()//创建双链表
{
    head* p;
    p=(head*)malloc(sizeof(head));
    p->length=0;
    p->next=NULL;
    return p;
}
void listinsert(head* p,int pos,int x)//单链表数据节点的插入
{
    node* temp;
    temp=(node*)malloc(sizeof(node));
    node* pcur=p->next;//定义一个指向第一个数据节点的指针;
    temp->val=x;
    if(p==NULL||pos<0||pos>p->length)
    {
        printf("listinsert():error\n");
        return;
    }
    if(p->length==0)
    {
        p->next=temp;
        temp->next=NULL;
        temp->pre=NULL;
        p->length++;
        return;
    }
    if(pos==0)
    {
        p->next=temp;
        temp->pre=NULL;
        temp->next=pcur;
        pcur->pre=temp;
        p->length++;
        return;
    }
    for(int i=1;i<pos;i++)//使pcur指向要插入的位置
    {
        pcur=pcur->next;
    }
    if(pos==p->length)
    {
        pcur->next=temp;
        temp->next=NULL;
        temp->pre=pcur;
    }
    else
    {
        temp->next=pcur->next;
        pcur->next->pre=temp;
        temp->pre=pcur;
        pcur->next=temp;
    }
    p->length++;
    return;
}
void listdelete(head* p,int x)
{
    node* temp=p->next;
    while(temp->val!=x&&temp!=NULL)
    {
        temp=temp->next;
    }
    if(temp->val!=x)
    {
        printf("listdelete():error");
        return;
    }
    node* pRe=temp->pre;
    node* pnext=temp->next;
    if(pRe==NULL)
    {
        p->next=pnext;
        pnext->pre=NULL;
    }
    else if(pnext==NULL)
    {
        pRe->next=NULL;
    }
    else
    {
        pRe->next=pnext;
        pnext->pre=pRe;
    }
    free(temp);
    p->length--;
    return;
}
void print(head* p)
{
    if(p==NULL)
    {
        printf("listprint():error\n");
        return;
    }
    node* temp=p->next;
    while(temp!=NULL)
    {
        printf("%d ",temp->val);
        temp=temp->next;
    }
    printf("\n");
    return;
}
int main()
{
    int number;
    head* p=listcreat();
    printf("请输入双链表数据节点初始个数\n");
    scanf("%d",&number);
    printf("请依次输入初始数据\n");
    int a[number];
    for(int i=0;i<number;i++)
    {
        scanf("%d",&a[i]);
    }
    for(int i=number-1;i>=0;i--)
    {
        listinsert(p,0,a[i]);
    }
    printf("输出初始链表\n");
    print(p);
    printf("请输入插入的位置和元素\n");
    int m,n;
    scanf("%d%d",&m,&n);
    listinsert(p,m,n);
    printf("输出插入后的链表\n");
    print(p);
    printf("请输入要删除的元素\n");
    int del;
    scanf("%d",&del);
    listdelete(p,del);
    printf("输出删除后的元素\n");
    print(p);
    return 0;
}

随便写点代码运行一下就是下面这个效果啦 (因为双链表和单链表的差别其实不大,而我也写过一篇单链表的博客,所以感觉没什么写的了,有什么遗漏的希望大家见谅)


请输入双链表数据节点初始个数

6

请依次输入初始数据

1 2 3 4 5 6

输出初始链表

1 2 3 4 5 6

请输入插入的位置和元素

6 7

输出插入后的链表

1 2 3 4 5 6 7

请输入要删除的元素

7

输出删除后的元素

1 2 3 4 5 6


相关文章
|
21天前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
33 1
|
1月前
|
存储 算法 搜索推荐
【趣学C语言和数据结构100例】91-95
本文涵盖多个经典算法问题的C语言实现,包括堆排序、归并排序、从长整型变量中提取偶数位数、工人信息排序及无向图是否为树的判断。通过这些问题,读者可以深入了解排序算法、数据处理方法和图论基础知识,提升编程能力和算法理解。
45 4
|
1月前
|
存储 机器学习/深度学习 搜索推荐
【趣学C语言和数据结构100例】86-90
本文介绍并用C语言实现了五种经典排序算法:直接插入排序、折半插入排序、冒泡排序、快速排序和简单选择排序。每种算法都有其特点和适用场景,如直接插入排序适合小规模或基本有序的数据,快速排序则适用于大规模数据集,具有较高的效率。通过学习这些算法,读者可以加深对数据结构和算法设计的理解,提升解决实际问题的能力。
43 4
|
1月前
|
存储 算法 数据处理
【趣学C语言和数据结构100例】81-85
本文介绍了五个经典算法问题及其C语言实现,涵盖图论与树结构的基础知识。包括使用BFS求解单源最短路径、统计有向图中入度或出度为0的点数、统计无向无权图各顶点的度、折半查找及二叉排序树的查找。这些算法不仅理论意义重大,且在实际应用中极为广泛,有助于提升编程能力和数据结构理解。
38 4
|
19天前
|
存储 算法 C语言
【C语言】深入浅出:C语言链表的全面解析
链表是一种重要的基础数据结构,适用于频繁的插入和删除操作。通过本篇详细讲解了单链表、双向链表和循环链表的概念和实现,以及各类常用操作的示例代码。掌握链表的使用对于理解更复杂的数据结构和算法具有重要意义。
144 6
|
23天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
44 5
|
21天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
50 1
|
19天前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
39 10
|
19天前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
42 9
|
19天前
|
存储 Unix Serverless
【C语言】常用函数汇总表
本文总结了C语言中常用的函数,涵盖输入/输出、字符串操作、内存管理、数学运算、时间处理、文件操作及布尔类型等多个方面。每类函数均以表格形式列出其功能和使用示例,便于快速查阅和学习。通过综合示例代码,展示了这些函数的实际应用,帮助读者更好地理解和掌握C语言的基本功能和标准库函数的使用方法。感谢阅读,希望对你有所帮助!
31 8