我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C语言双向链表的原理与使用操作

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C语言双向链表的原理与使用操作

一.引入

我们在单链表中,有了next指针,这个指针是用来指向下一个节点的,如果我们需要查找下一个结点的时间复杂度为o(1),如果我们需要查找上一个节点的时候,那么时间复杂度就变为o(n)了,需要从头进行遍历一遍;这时我们就会想:如果可以向前查找就方便了许多,因此我们引入了双向链表。

二.双向链表的定义

双向链表:是在单链表的每个节点中,再设置一个指向前驱结点的指针域。

顾名思义就是链表由单向的变成了双向的,每一个节点由原来的一个指针变为两个指针,一个用来指向直接后继,另一个用来指向直接前驱。

三.双向链表与单链表对比

通过对比可以更好认识二者的联系与区别。

3.1图示对比

单链表:

双向链表:

3.2代码对比

单链表代码如下:

typedef struct Node{ //定义单链表结点类型
	int data; //数据域,可以是别的各种数据类型
	struct Node *next; //指针域
}LNode, *LinkList;

双向链表代码如下:

typedef struct DulNode{
	int data;			// 	数据域
	struct DulNode *prior;		//  向前的指针
	struct DulNode *next;		//  向后的指针
}DulNode,*DuLinkList;

四.双向链表的操作

双向链表是单链表中扩展出来的结构,所以有很多的操作是和单链表相同的,如求长度,查找元素,获取一个元素,这里我们对双向链表进行创建,插入,删除,销毁的一系列操作。

4.1双向链表的创建

双向链表在初始化时,要给首尾两个节点分配内存空间。成功分配后,需要将首节点的prior指针和尾节点的next指针都指向NULL,这是十分关键的一步,因为这是之后用来判断空表的条件。并且当链表为空时,要将首节点的next指向尾节点,尾节点的prior指向首节点。

pElem CreatList(){
	pElem head = (pElem)malloc( sizeof(eElem) );
	assert( head != NULL );		//进行断言
	head->next = head->prior = NULL;//初始化链表指针置空
	return head;
}

4.2双向链表的插入

双向链表的插入其实并不复杂,只是在原有单链表的基础上多了连接一个向前的指针而已。但是需要注意的是操作的顺序很重要,不可以写反了。

以下面这个为例,假设存储元素e的结点为s,要实现将结点s插入到结点p和p->next之间

核心代码就只有以下四行:

s->prior=p; //把p赋值给s的前驱
s->next=p->next;// 把p->next赋值给s的后继
p->next->prior=s;// 把s赋值给p->next的前驱
p->next=s;   //把s赋值给p的后继

切记顺序不可以记错 在写代码的时候可以将操作步骤画出来,理清实施步骤的顺序。

4.3双向链表的删除

如果将插入操作的原理理解后,那么删除就很好理解了。

删除只需要两个步骤:

核心代码只有三行:

p->prior->next=p->next; //把p->next赋值给p->prior的后继
p->next->prior=p->prior;//把p->prior赋值给p->next的前驱
free(p); //释放结点

4.4双向链表的销毁

销毁一个双向链表的操作同单链表的相似。指针不断向后运动,每运动一个结点,释放上一个结点。

代码如下:

void DestroyList( pElem head ){
	pElem tmp;
	while( head->next != NULL ){
		tmp = head;		//  指针不断后移
		head = head->next;
		free(tmp);
	}
	free(head);
}

五.总结

双向链表相比于单链表来说,是更复杂一些的,毕竟多了一个prior指针,对于插入和删除需要特别注意这两种操作的核心思想以及操作顺序。另外双向链表,带来了方便,可以有效提高算法的时间性能。

六.全部代码

这里引用一位大佬写的代码,将头插法和尾插法创建表都写了,写的很细节,很清楚大家可以参考一下。

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int status;
typedef int elemtype;
typedef struct node{
    elemtype data;
    struct node * next;
    struct node * prior;
}node;
typedef struct node* dlinklist;
status visit(elemtype c){
    printf("%d ",c);
}

status initdlinklist(dlinklist * head,dlinklist * tail){
    (*head)=(dlinklist)malloc(sizeof(node));
    (*tail)=(dlinklist)malloc(sizeof(node));
    if(!(*head)||!(*tail))
        return ERROR;
     
    (*head)->prior=NULL;
    (*tail)->next=NULL;
    
    (*head)->next=(*tail);
    (*tail)->prior=(*head);
}

status emptylinklist(dlinklist head,dlinklist tail){
    if(head->next==tail)
       return TRUE;
    else
       return FALSE;
} 
 
status createdlinklisttail(dlinklist head,dlinklist tail,elemtype data){
    dlinklist pmove=tail,pinsert;
    pinsert=(dlinklist)malloc(sizeof(node));
    if(!pinsert)
         return ERROR;
    pinsert->data=data;
    pinsert->next=NULL;
    pinsert->prior=NULL;
    tail->prior->next=pinsert;
    pinsert->prior=tail->prior;
    pinsert->next=tail;
    tail->prior=pinsert;
} 
 
status createdlinklisthead(dlinklist head,dlinklist tail,elemtype data){
    dlinklist pmove=head,qmove=tail,pinsert;
    pinsert=(dlinklist)malloc(sizeof(node));
    if(!pinsert)
        return ERROR;
    else{
        pinsert->data=data;
        pinsert->prior=pmove;
        pinsert->next=pmove->next;
        pmove->next->prior=pinsert;
        pmove->next=pinsert;
    }
}
 
status traverselist(dlinklist head,dlinklist tail){
    
    dlinklist pmove=head->next;
    while(pmove!=tail){
        visit(pmove->data);
        pmove=pmove->next;
    }
    printf("\n");
}

status locateelem(dlinklist head,dlinklist tail,elemtype data){
    dlinklist pmove=head->next;
    int pos=1;
    while(pmove&&pmove->data!=data){
        pmove=pmove->next;
        pos++;
    }
    return pos;
}

status listlength(dlinklist head,dlinklist tail){
    dlinklist pmove=head->next;
    int length=0;
    while(pmove!=tail){
        pmove=pmove->next;
        length++;
    }
    return length;
}

status inverse(dlinklist head,dlinklist tail){
    dlinklist pmove=tail->prior;
    while(pmove!=head){
        visit(pmove->data);
        pmove=pmove->prior;
    }
    printf("\n");
}

status deleteelem(dlinklist head,dlinklist tail,int pos,elemtype *data){
    int i=1;
    dlinklist pmove=head->next;
    while(pmove&&i<pos){
        pmove=pmove->next;
        i++;
    }
    if(!pmove||i>pos){
        printf("输入数据非法\n");
        return ERROR;
    }
    else{
        *data=pmove->data;
        pmove->next->prior=pmove->prior;
        pmove->prior->next=pmove->next;
        free(pmove);
    }
}

status inserttail(dlinklist head,dlinklist tail,elemtype data){
    dlinklist pinsert;
    pinsert=(dlinklist)malloc(sizeof(node));
    pinsert->data=data;
    pinsert->next=NULL;
    pinsert->prior=NULL;
    tail->prior->next=pinsert;
    pinsert->prior=tail->prior;
    pinsert->next=tail;
    tail->prior=pinsert;
    return OK;
} 
int main(void){
    dlinklist head,tail;
    int i=0;
    elemtype data=0;
    initdlinklist(&head,&tail);
    if(emptylinklist(head,tail))
        printf("链表为空\n");
    else
        printf("链表不为空\n");
    printf("头插法创建链表\n"); 
    for(i=0;i<10;i++){
        createdlinklisthead(head,tail,i);
    }
    traverselist(head,tail);
    for(i=0;i<10;i++){
        printf("表中值为%d的元素的位置为",i); 
        printf("%d位\n",locateelem(head,tail,i));
    }
    printf("表长为%d\n",listlength(head,tail));
    printf("逆序打印链表");
    inverse(head,tail);
    for(i=0;i<10;i++){
        deleteelem(head,tail,1,&data);
        printf("被删除的元素为%d\n",data);
    }
    traverselist(head,tail);
    if(emptylinklist(head,tail))
        printf("链表为空\n");
    else
        printf("链表不为空\n");
        printf("尾插法创建链表\n");
    for(i=0;i<10;i++){
        //inserttail(head,tail,i);
        createdlinklisttail(head,tail,i);
    }
    traverselist(head,tail);
    printf("逆序打印链表");
    inverse(head,tail);
}

到此这篇关于C语言双向链表的原理与使用操作的文章就介绍到这了,更多相关C语言双向链表内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C语言双向链表的原理与使用操作

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C语言中双链表的基本操作

这篇文章主要介绍了C语言中双链表的基本操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-02-05

C语言中如何实现单向链表的增删查改操作

这篇文章主要介绍了C语言中如何实现单向链表的增删查改操作,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。前言链表是线性表的链式存储结构,它可以以O(1)的时间复杂度进行插入或者
2023-06-25

C语言无头单向非循环链表的操作方法有哪些

这篇文章主要介绍“C语言无头单向非循环链表的操作方法有哪些”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C语言无头单向非循环链表的操作方法有哪些”文章能帮助大家解决问题。链表引入问:上次我们看了顺序
2023-06-30

C语言中互斥锁与自旋锁及原子操作使用浅析

今天不整GO语言,我们来分享一下以前写的C语言代码,来看看互斥锁、自旋锁和原子操作的demo,示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
2023-01-11

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录