zoukankan      html  css  js  c++  java
  • C语言实现双向链表

    目前我们所学到的链表,无论是动态链表还是静态链表,表中各节点中都只包含一个指针(游标),且都统一指向直接后继节点,通常称这类链表为单向链表(或单链表)。

    虽然使用单链表能 100% 解决逻辑关系为 “一对一” 数据的存储问题,但在解决某些特殊问题时,单链表并不是效率最优的存储结构。比如说,如果算法中需要大量地找某指定结点的前趋结点,使用单链表无疑是灾难性的,因为单链表更适合 “从前往后” 找,而 “从后往前” 找并不是它的强项。

    为了能够高效率解决类似的问题,本篇文章我们一起来讨论双向链表(简称双链表)。
    从名字上理解双向链表,即链表是 “双向” 的,如下图所示:
    图一

    双向,指的是各节点之间的逻辑关系是双向的,但通常头指针只设置一个,除非实际情况需要。

    从上图中可以看到,双向链表中各节点包含以下 3 部分信息(如下图 所示):

    • 指针域 :用于指向当前节点的直接前驱节点;
    • 数据域 :用于存储数据元素。
    • 指针域:用于指向当前节点的直接后继节点;
      在这里插入图片描述
      因此,双链表的节点结构用 C 语言实现为:
    typedef struct Node
    {
    	struct Node * prior;//指向直接前趋
    	int data;
    	struct Node * next;//指向直接后继
    }Node;
    

    双向链表的创建

    同单链表相比,双链表仅是各节点多了一个用于指向直接前驱的指针域。因此,我们可以在单链表的基础轻松实现对双链表的创建。

    需要注意的是,与单链表不同,双链表创建过程中,每创建一个新节点,都要与其前驱节点建立两次联系,分别是:

    1. 将新节点的 prior 指针指向直接前驱节点;
    2. 将直接前驱节点的 next 指针指向新节点;

    这里给出创建双向链表的 C 语言实现代码:

    Node* initNode(Node * head){
    	head=(Node*)malloc(sizeof(Node));//创建链表第一个结点(首元结点)
    	head->prior=NULL;
    	head->next=NULL;
    	head->data=1;
    	Node * list=head;
    	for (int i=2; i<=3; i++) {
    		//创建并初始化一个新结点
    		Node * body=(Node*)malloc(sizeof(Node));
    		body->prior=NULL;
    		body->next=NULL;
    		body->data=i;
    	  
    		list->next=body;//直接前趋结点的next指针指向新结点
    		body->prior=list;//新结点指向直接前趋结点
    		list=list->next;
    	}
    	return head;
    }
    

    我们可以尝试着在 main 函数中输出创建的双链表,C 语言代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    //节点结构
    typedef struct Node{
    	struct Node * prior;
    	int data;
    	struct Node * next;
    }Node;
    //双链表的创建函数
    Node* initNode(Node * head);
    //输出双链表的函数
    void display(Node * head);
    
    int main() {
    	//创建一个头指针
    	Node * head=NULL;
    	//调用链表创建函数
    	head=initNode(head);
    	//输出创建好的链表
    	display(head);
    	//显示双链表的优点
    	printf("链表中第 4 个节点的直接前驱是:%d",head->next->next->next->prior->data);
    	return 0;
    }
    Node* initNode(Node * head){
    	//创建一个首元节点,链表的头指针为head
    	head=(Node*)malloc(sizeof(Node));
    	//对节点进行初始化
    	head->prior=NULL;
    	head->next=NULL;
    	head->data=1;
    	//声明一个指向首元节点的指针,方便后期向链表中添加新创建的节点
    	Node * list=head;
    	for (int i=2; i<=5; i++) {
    		//创建新的节点并初始化
    		Node * body=(Node*)malloc(sizeof(Node));
    		body->prior=NULL;
    		body->next=NULL;
    		body->data=i;
    
    		//新节点与链表最后一个节点建立关系
    		list->next=body;
    		body->prior=list;
    		//list永远指向链表中最后一个节点
    		list=list->next;
    	}
    	//返回新创建的链表
    	return head;
    }
    void display(Node * head){
    	Node * temp=head;
    	while (temp) {
    		//如果该节点无后继节点,说明此节点是链表的最后一个节点
    		if (temp->next==NULL) {
    			printf("%d
    ",temp->data);
    		}else{
    			printf("%d <-> ",temp->data);
    		}
    		temp=temp->next;
    	}
    }
    

    程序运行结果:

    1 <-> 2 <-> 3 <-> 4 <-> 5
    链表中第 4 个节点的直接前驱是:3

    双向链表基本操作

    下面继续讨论有关双向链表的一些基本操作,即如何在双向链表中添加、删除、查找或更改数据元素。

    创建好的双向链表如下图所示:
    在这里插入图片描述

    双向链表添加节点

    根据数据添加到双向链表中的位置不同,可细分为以下 3 种情况:
    添加至表头
    将新数据元素添加到表头,只需要将该元素与表头元素建立双层逻辑关系即可。

    换句话说,假设新元素节点为 temp,表头节点为 head,则需要做以下 2 步操作即可:

    1. temp->next=head; head->prior=temp;
    2. 将 head 移至 temp,重新指向新的表头;

    例如,将新元素 7 添加至双链表的表头,则实现过程如下图 所示:
    在这里插入图片描述
    添加至表的中间位置
    同单链表添加数据类似,双向链表中间位置添加数据需要经过以下 2 个步骤,如下图 所示:

    1. 新节点先与其直接后继节点建立双层逻辑关系;
    2. 新节点的直接前驱节点与之建立双层逻辑关系;

    lxQWmd.gif
    添加至表尾
    与添加到表头是一个道理,实现过程如下(如下图 所示):

    1. 找到双链表中最后一个节点;
    2. 让新节点与最后一个节点进行双层逻辑关系;

    lxQ2OH.gif

    因此,双向链表添加数据的 C 语言代码,参考代码如下:

    Node * insertNode(Node * head,int data,int add){
    	//新建数据域为data的结点
    	Node * temp=(Node*)malloc(sizeof(Node));
    	temp->data=data;
    	temp->prior=NULL;
    	temp->next=NULL;
    	//插入到链表头,要特殊考虑
    	if (add==1) {
    		temp->next=head;
    		head->prior=temp;
    		head=temp;
    	}else{
    		Node * body=head;
    		//找到要插入位置的前一个结点
    		for (int i=1; i<add-1; i++) {
    			body=body->next;
    		}
    		//判断条件为真,说明插入位置为链表尾
    		if (body->next==NULL) {
    			body->next=temp;
    			temp->prior=body;
    		}else{
    			body->next->prior=temp;
    			temp->next=body->next;
    			body->next=temp;
    			temp->prior=body;
    		}
    	}
    	return head;
    }
    
    双向链表删除节点

    双链表删除结点时,只需遍历链表找到要删除的结点,然后将该节点从表中摘除即可。
    例如,删除上面图中的元素2,如下图所示:
    lxliX4.gif]
    双向链表删除节点的 C 语言实现代码如下:

    //删除结点的函数,data为要删除结点的数据域的值
    Node * delNode(Node * head,int data){
    	Node * temp=head;
    	//遍历链表
    	while (temp) {
    		//判断当前结点中数据域和data是否相等,若相等,摘除该结点
    		if (temp->data==data) {
    			temp->prior->next=temp->next;
    			temp->next->prior=temp->prior;
    			free(temp);
    			return head;
    		}
    		temp=temp->next;
    	}
    	printf("链表中无该数据元素");
    	return head;
    }
    
    双向链表查找节点

    通常,双向链表同单链表一样,都仅有一个头指针。因此,双链表查找指定元素的实现同单链表类似,都是从表头依次遍历表中元素。

    C 语言实现代码为:

    //head为原双链表,elem表示被查找元素
    int selectElem(Node * head,int elem){
    //新建一个指针t,初始化为头指针 head
    	Node * t=head;
    	int i=1;
    	while (t) {
    		if (t->data==elem) {
    			return i;
    		}
    		i++;
    		t=t->next;
    	}
    	//程序执行至此处,表示查找失败
    	return -1;
    }
    
    双向链表更改节点

    更改双链表中指定结点数据域的操作是在查找的基础上完成的。实现过程是:通过遍历找到存储有该数据元素的结点,直接更改其数据域即可。

    实现此操作的 C 语言实现代码如下:

    //更新函数,其中,add 表示更改结点在双链表中的位置,newElem 为新数据的值
    Node *amendElem(Node * p,int add,int newElem){
    	Node * temp=p;
    	//遍历到被删除结点
    	for (int i=1; i<add; i++) {
    		temp=temp->next;
    	}
    	temp->data=newElem;
    	return p;
    }
    

    基本上写到这里这篇关于C语言实现双向链表的文章就结束了,总的实现代码已经push到github,喜欢的小伙伴欢迎Star!传送门,小编如果有什么写的不好的地方,欢迎大家留言提出来,多多指教,如果还想了解其他语言实现的数据结构的相关算法,欢迎来我的个人博客相逢了解更多哟!明天继续更新C++语言实现双向链表,坚持就是胜利!加油!

  • 相关阅读:
    WSP部署错误—SharePoint管理框架中的对象“SPSolutionLanguagePack Name=0”依赖其他不存在的对象
    Elevate Permissions To Modify User Profile
    Error with Stsadm CommandObject reference not set to an instance of an object
    ASP.NET MVC3添加Controller时没有Scaffolding options
    测试使用Windows Live Writer写日志
    配置TFS 2010出现错误—SQL Server 登录的安全标识符(SID)与某个指定的域或工作组帐户冲突
    使用ADO.NET DbContext Generator出现错误—Unable to locate file
    CSS
    HTML DIV标签
    数据库
  • 原文地址:https://www.cnblogs.com/xiangjunhong/p/12482462.html
Copyright © 2011-2022 走看看