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

目录
  • 一.引入
  • 二.双向链表的定义
  • 三.双向链表与单链表对比
    • 3.1图示对比
    • 3.2代码对比
  • 四.双向链表的操作
    • 4.1双向链表的创建
    • 4.2双向链表的插入
    • 4.3双向链表的删除
    • 4.4双向链表的销毁
  • 五.总结
  • 六.全部代码

一.引入

我们在单链表中,有了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){
        printf("%d ",pmove->data);
        pmove=pmove->next;
    }
    printf("\n");
    return OK;*/
    dlinklist pmove=head->next;
    while(pmove!=tail){
        visit(pmove->data);
        pmove=pmove->next;
    }
    printf("\n");
}
/*返回第一个值为data的元素的位序*/
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");
}
/*删除链表中第pos个位置的元素,并用data返回*/
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语言双向链表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言算法学习之双向链表详解

    目录 一.练习题目 二.算法思路 1.设计浏览器历史记录 2.扁平化多级双向链表 3.展平多级双向链表 4.二叉搜索树与双向链表 一.练习题目 题目链接 难度 1472. 设计浏览器历史记录 ★★★☆☆ 430. 扁平化多级双向链表 ★★★☆☆ 剑指 Offer II 028. 展平多级双向链表 ★★★☆☆ 剑指 Offer 36. 二叉搜索树与双向链表 ★★★★☆ 二.算法思路 1.设计浏览器历史记录 1.这是一个模拟题: 2.初始化生成一个头结点,记录一个当前结点: 3.向前 和 向后 是两

  • C语言 数据结构双向链表简单实例

    双向链表的基本操作 1.利用尾插法建立一个双向链表. 2.遍历双向链表. 3.实现双向链表中删除一个指定元素. 4.在非递减有序双向链表中实现插入元素e仍有序算法. 5.判断双向链表中元素是否对称若对称返回1否则返回0. 6.设元素为正整型,实现算法把所有奇数排列在偶数之前. 7.在主函数中设计一个简单的菜单调试上述算法. 实例代码: //排序的时候因为没有说明奇数和偶数需不需要各自再排序,我就没有排序,只是将奇数放在偶数后面. //创建链表的时候,因为这个实验没有要求输出链表的长度,所以我就输

  • C语言中双向链表和双向循环链表详解

    双向链表和双向循环链表 和单向链表相比,多了一个前驱结点.如果他为空,那么next和prior都指向自己.而对于双循环链表,只需要最后一个元素的next指向head->next,head->next的prior指向最后一个节点即可. 插入操作 新节点s插入链表,s->next给p结点,s->prior给p->prior,然后,p->prior->next指向s,p->prior再指向s.顺序需要注意 s->next = p; s->prior =

  • C语言双向链表的表示与实现实例详解

    1.概述: C语言中一种更复杂的链表是"双向链表"或"双面链表".其表中的每个节点有两个连接:一个指向前一个节点,(当这个"连接"为第一个"连接"时,指向空值或者空列表):而另一个指向下一个节点,(当这个"连接"为最后一个"连接"时,指向空值或者空列表) 一个双向链表有三个整数值: 数值, 向后的节点链接, 向前的节点链接 在一些低级语言中, XOR-linking 提供一种在双向链表中

  • C语言实现双向链表

    这个小代码是我凭自己对指针和链表的理解和认识,自己实现的,没有参考其他人的代码,如果有相同的地方,那真的只是巧合,代码我在ubuntu 15.04下测试通过,可能存在很多错误和漏洞. doublelist.c /************************************************************************* > File Name: doublelist.c > Author: ChenYiLiang > Mail: chenyilian

  • C语言类的双向链表详解

    目录 前言 双向链表的定义 双向链表的创建 节点的创建 双向链表节点查找 双向链表的插入 双向链表的节点删除 双向链表的删除 总结 前言 链表(linked list)是一种这样的数据结构,其中的各对象按线性排列.数组的线性顺序是由数组下标决定的,然而于数组不同的是,链表的各顺序是由链表中的指针决定的. 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点.一般我们都构造双向循

  • C语言数据结构 双向链表的建立与基本操作

    C语言数据结构 双向链表的建立与基本操作 双向链表比单链表有更好的灵活性,其大部分操作与线性表相同.下面总结双向链表与单链表之间的不同之处及我在实现过程中所遇到的问题. 1.双向链表的建立 双向链表在初始化时,要给首尾两个节点分配内存空间.成功分配后,要将首节点的prior指针和尾节点的next指针指向NULL,这是十分关键的一步,因为这是之后用来判断空表的条件.同时,当链表为空时,要将首节点的next指向尾节点,尾节点的prior指向首节点. 2.双向链表的插入操作 由于定义双向链表时指针域中

  • C语言之双向链表详解及实例代码

    1,双向链表简介. 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点.一般我们都构造双向循环链表. 2,例子要求: 完成双向链表的插入.删除以及查找,将学生管理系统使用的数组,以双向链表的方式实现,能够支持无限制的学生人数的增删改查以及保存. 3,代码实现. #include <stdio.h> #include <string.h> #include <

  • C语言实现数据结构和双向链表操作

    数据结构  双向链表的实现 双向链表中的每一个结点都含有两个指针域,一个指针域存放其后继结点的存储地址,另一个指针域则存放其前驱结点的存储地址. 双向链表结点的类型描述: //双向链表的类型描述 typedef int ElemType; typedef struct node{ ElemType data; struct node *prior,*next; }DuLNode,*DuLinkList; 其中,prior域存放的是其前驱结点的存储地址,next域存放的是其后继结点的存储地址. 双

  • C语言双向链表实现根据使用频率安排元素位置的功能实例代码

    C语言双向链表应用 前言: 平时使用音乐播放器时,播放列表中的歌曲可以很方便地进行增添,删除,去重等操作.但其本质都可以抽象成一个双向链表.为了更方便用户的使用,我认为还可以增加一个将最常播放的音乐放在播放列表的头部的功能,那么如何实现呢? 请看代码: #include<stdio.h> #include<stdlib.h> #include<time.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALS

随机推荐