数据结构8: 双向链表(双向循环链表)的建立及C语言实现
之前接触到的链表都只有一个指针,指向直接后继,整个链表只能单方向从表头访问到表尾,这种结构的链表统称为 “单向链表”或“单链表”。
如果算法中需要频繁地找某结点的前趋结点,单链表的解决方式是遍历整个链表,增加算法的时间复杂度,影响整体效率。
为了快速便捷地解决这类问题,在单向链表的基础上,给各个结点额外配备一个指针变量,用于指向每个结点的直接前趋元素。这样的链表被称为“双向链表”或者“双链表”。
双链表中的结点
双向链表中的结点有两个指针域,一个指向直接前趋,一个指向直接后继。(链表中第一个结点的前趋结点为NULL,最后一个结点的后继结点为NULL)

结点的具体构成:
typedef struct line
{
struct line *prior; //指向直接前趋
int data;
struct line *next; //指向直接后继
}line;
创建双向链表并初始化
双向链表创建的过程中,每一个结点需要初始化数据域和两个指针域,一个指向直接前趋结点,另一个指向直接后继结点。
例如,创建一个双向链表line(1,2,3):

实现代码:
line* initLine(line *head)
{
head = (line*)malloc(sizeof(line)); //创建链表第一个结点(首元结点)
head->prior = NULL;
head->next = NULL;
head->data = ;
line *list = head;
for (int i=; i<=; i++)
{
//创建并初始化一个新结点
line *body = (line*)malloc(sizeof(line));
body->prior = NULL;
body->next = NULL;
body->data = i;
list->next = body;//直接前趋结点的next指针指向新结点
body->prior = list;//新结点指向直接前趋结点
list = list->next;
}
return head;
}
双向链表中插入结点
比如在(1,2,3)中插入一个结点 4,变成(1,4,2,3)。
实现效果图:

实现代码:
line *insertLine(line * head, int data, int add)
{
//新建数据域为data的结点
line * temp = (line*)malloc(sizeof(line));
temp->data = data;
temp->prior = NULL;
temp->next = NULL;
//插入到链表头,要特殊考虑
if (add == )
{
temp->next = head;
head->prior = temp;
head = temp;
}
else
{
line *body = head;
//找到要插入位置的前一个结点
for (int i=; i<add-; 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;
}
双向链表中删除节点
双链表删除结点时,直接遍历链表,找到要删除的结点,然后利用该结点的两个指针域完成删除操作。
例如,在(1,4,2,3)中删除结点 2:
//删除结点的函数,data为要删除结点的数据域的值
line *delLine(line *head, int data)
{
line *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;
}
双向链表中的查找和更改操作
双向链表的查找操作和单链表的实现方法完全一样,从链表的头结点或者首元结点开始遍历,这里不做过多解释。
更改链表中某结点的数据域的操作是在查找的基础上完成的。通过遍历找到存储有该数据元素的结点后,直接更改其数据域就可以。
本节的完整代码
#include <stdio.h>
#include <stdlib.h>
typedef struct line
{
struct line *prior;
int data;
struct line *next;
}line;
line *initLine(line *head);
line *insertLine(line *head, int data, int add);
line *delLine(line *head, int data);
void display(line *head);
int main()
{
line *head = NULL;
head=initLine(head);
head=insertLine(head, , );
display(head);
head=delLine(head, );
display(head);
return ;
}
line *initLine(line * head)
{
head = (line*)malloc(sizeof(line));
head->prior = NULL;
head->next = NULL;
head->data = ;
line *list = head;
for (int i=; i<=; i++)
{
line *body = (line*)malloc(sizeof(line));
body->prior = NULL;
body->next = NULL;
body->data = i;
list->next = body;
body->prior = list;
list = list->next;
}
return head;
}
line *insertLine(line *head, int data, int add)
{
//新建数据域为data的结点
line *temp = (line*)malloc(sizeof(line));
temp->data = data;
temp->prior = NULL;
temp->next = NULL;
//插入到链表头,要特殊考虑
if (add == )
{
temp->next = head;
head->prior = temp;
head = temp;
}
else
{
line *body = head;
//找到要插入位置的前一个结点
for (int i=; i<add-; 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;
}
line *delLine(line *head, int data)
{
line *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;
}
//输出链表的功能函数
void display(line *head)
{
line *temp = head;
while (temp)
{
if (temp->next == NULL)
{
printf("%d\n",temp->data);
}
else
{
printf("%d->",temp->data);
}
temp=temp->next;
}
} 总结
双向链表和单链表唯一的不同在于结构中多了一个指向直接前趋的指针,其他完全一样。如果问题中需要频繁的调取当前结点的前趋结点,那使用双向链表的数据结构为最佳方案。
补:双向链表和循环链表的结合体
约瑟夫环问题其实还可以这样玩:如果顺时针报数,有人出列后,顺时针找出出列位置的下一个人,开始反方向(也就是逆时针)报数,有人出列后,逆时针找出出列位置的下一个人,开始顺时针报数。依次重复,直至最后一个出列。
例如,还是从编号为 3 的开始数,数到 2 的人出列:

新玩法的出列顺序为:
首先顺时针转,4 数 2,所以 4 出列;
顺时针找到下一个人为 5,开始逆时针转,3 数 2,所以 3 出列;
逆时针找到下一个人为 2,开始顺时针转,5 数 2,所以 5 出列;
顺时针找到下一个人为 1,开始逆时针转,2 数 2,所以 2 出列;
最后只剩下 1,所以 1 自己出列。
对于新的约瑟夫环问题,需要将循环链表和双向链表结合使用,组成:双向循环链表。
有兴趣的可以尝试编码解决新的约瑟夫环问题。
数据结构8: 双向链表(双向循环链表)的建立及C语言实现的更多相关文章
- 【C语言教程】“双向循环链表”学习总结和C语言代码实现!
双向循环链表 定义 双向循环链表和它名字的表意一样,就是把双向链表的两头连接,使其成为了一个环状链表.只需要将表中最后一个节点的next指针指向头节点,头节点的prior指针指向尾节点,链表就能成环儿 ...
- JS数据结构第三篇---双向链表和循环链表之约瑟夫问题
一.双向链表 在上文<JS数据结构第二篇---链表>中描述的是单向链表.单向链表是指每个节点都存有指向下一个节点的地址,双向链表则是在单向链表的基础上,给每个节点增加一个指向上一个节点的地 ...
- 双向链表、双向循环链表的JS实现
关于链表简介.单链表.单向循环链表.JS中的使用以及扩充方法: 单链表.循环链表的JS实现 关于四种链表的完整封装: https://github.com/zhuwq585/Data-Structu ...
- 1.Go语言copy函数、sort排序、双向链表、list操作和双向循环链表
1.1.copy函数 通过copy函数可以把一个切片内容复制到另一个切片中 (1)把长切片拷贝到短切片中 package main import "fmt" func main() ...
- 1.Go-copy函数、sort排序、双向链表、list操作和双向循环链表
1.1.copy函数 通过copy函数可以把一个切片内容复制到另一个切片中 (1)把长切片拷贝到短切片中 ? 1 2 3 4 5 6 7 8 9 10 11 12 package main imp ...
- java与数据结构(4)---java实现双向循环链表
线性表之链式存储结构双向循环链表 双向循环链表:每个结点包含了数据.直接前驱地址指针和直接后驱地址指针,头结点的直接前驱指向尾结点,尾结点的直接后驱指向头结点,头尾相连构成一个可正可反的圆环.可以形象 ...
- java数据结构-06双向循环链表
双向循环链表跟单向链表一样,都是头尾相连,不过单向是尾指向头,双向是头尾互相指,可以从前往后查,也可以从后往前查 无头结点的双向循环链表 public class CircleLinkedList&l ...
- C++实现双向循环链表
本次博文是关于利用C++模板的方式实现的双向循环链表以及双向循环链表的基本操作,在之前的博文C++语言实现双向链表中,已经给大家分析了双向循环链表的结构,并以图示的方式给大家解释了双向循环链表的基本操 ...
- C语言通用双向循环链表操作函数集
说明 相比Linux内核链表宿主结构可有多个链表结构的优点,本函数集侧重封装性和易用性,而灵活性和效率有所降低. 可基于该函数集方便地构造栈或队列集. 本函数集暂未考虑并发保护. 一 ...
随机推荐
- 2015.3.12 C#运用正则表达式点滴
Regex reg = new Regex(@"\w{1,}@\w{1,}\.(com)?(net)?"); //匹配(1个或多个)(字母或数字或下滑线)@ (1个或多个)(字母或 ...
- 简单的触发黑名单阻断演示 control+c
#include "stdafx.h"#include <signal.h>#include <windows.h> #include <iostre ...
- xcode 编译报错“Cannot create __weak reference in file using manual reference counting”解决办法<转>
http://blog.csdn.net/ouq68/article/details/51003876 解决方法: Please set ‘Weak References in Manual Reta ...
- 我的第一个Socket程序-SuperSocket使用入门(一)
第一次使用Socket,遇到过坑,也涨过姿势,网上关于SuperSocket的教程基本都停留在官方给的简单demo上,实际使用还是会碰到一些问题,所以准备写两篇博客,分别来介绍SuperSocket以 ...
- 支撑矢量机SVM
1.线性SVM 首先,回顾一下SVM问题的定义,如下: 线性约束很烦,不方便优化,是否有一种方法可以将线性约束放到优化问题本身,这样就可以无拘无束的优化,而不用考虑线性约束了.其对应的拉格朗日对偶形式 ...
- MyBatis总结四:配置文件xml详解
XML 映射配置文件 MyBatis 的配置文件包含了影响 MyBatis 行为甚深的设置(settings)和属性(properties)信息.文档的顶层结构如下: configuration 配置 ...
- FileZilla Server下通过别名设置虚拟目录
说明:FileZilla Server 的虚拟目录设置与其它 FTP 服务器软件有所不同.在 FileZilla Server 中设置虚拟目录,必须采用 FTP 根目录 + 虚拟目录名的形式来进行.比 ...
- ZROI2018普转提day2t2
传送门 分析 我们发现2R+C实际就相当于R行C列的子集的个数.因此我们可以将所有集合的子集个数转换为每个集合属于的集合的个数.所以我们可以求出: 这个式子的意义为对于选i行j列的情况的所有方案乘上i ...
- backstop bsg6
- SDUT 3373 数据结构实验之查找一:二叉排序树
数据结构实验之查找一:二叉排序树 Time Limit: 400MS Memory Limit: 65536KB Submit Statistic Problem Description 对应给定的一 ...