数据结构之双向链表(包含双向循环链表)

双向(循环)链表是线性表的链式存储结构的又一种形式。

在之前已经讲述了单向链表循环链表。相比于单向链表只能从头结点出发遍历整个链表的局限性,循环链表使得可以从任意一个结点遍历整个链表

但是,不管单向链表也好,循环链表也罢,都只能从一个方向遍历链表,即只能查找结点的下一个结点(后继结点),而不能查找结点的上一个结点(前驱结点)。鉴于上述问题,引入了双向链表。由于双向循环链表包含双向链表的所有功能操作。因此,我们只讲述双向循环链表。

与单向链表不同,双向链表的结点构造如下图所示。即一个结点由三个部分组成,数据域DATA,左指针域llink和右指针域rlink。其中,数据域用来存放结点数据信息,左指针域用来指向前驱结点,右指针域用来指向后继结点。双向循环链表有一个特性:p->llink->rlink = p->rlink->llink = p。

用来表示双向链表结点的代码如下:

public class Node {
	private Object data;
	private Node lLink, rLink;

	//构造函数
	public Node() {
		this.data = null;
		this.lLink = null;
		this.rLink = null;
	}
	//构造函数重载
	public Node(Object data) {
		this.data = data;
		this.lLink = null;
		this.rLink = null;
	}
	public Node(Object data, Node lLink, Node rLink) {
		this.data = data;
		this.lLink = lLink;
		this.rLink = rLink;
	}

	//读结点数据
	public Object getData() {
		return data;
	}
	//写结点数据
	public void setData(Object data) {
		this.data = data;
	}
	//获取结点的前驱结点
	public Node getLeftLink() {
		return lLink;
	}
	//获取结点的后继结点
	public Node getRightLink() {
		return rLink;
	}
	//设置结点的前驱结点
	public void setLeftLink(Node lLink) {
		this.lLink = lLink;
	}
	//设置结点的后继结点
	public void setRightLink(Node rLink) {
		this.rLink = rLink;
	}
}

在带有头结点的双向循环链表类代码如下。在代码示例中,仅介绍了双向循环链表的创建、插入元素、删除元素和打印链表的操作。

public class DoubleLink {
	/**
	 * 带有头结点的双向循环链表的头结点
	 */
	private Node headNode = new Node();
	/**
	 * 链表长度
	 */
	private int length = 0;

	/**
	 * 创建一个带有头结点的双向链表
	 *
	 * @param datas 数组,用来表示双向链表中各个结点的数据域
	 */
	public void createDoubleLink(Object[] datas) {
		Node p, r;
		r = headNode;
		for(int i=0; i<datas.length; i++) {
			p = new Node(datas[i]);
			r.setRightLink(p);
			p.setLeftLink(r);
			p.setRightLink(headNode);
			r = p;
			length++;
		}
		headNode.setLeftLink(r);
	}

	/**
	 * 打印带有头结点的双向循环链表
	 */
	public void printDoubleLink() {
		Node p = headNode.getRightLink();
		while(p != headNode) {
			System.out.print(p.getData() + " ");
			p = p.getRightLink();
		}
		System.out.println();
	}

	/**
	 * 在双向循环链表的pos位置后面插入数据域为item的结点
	 *
	 * @param pos 表示插入元素的位置。计算位置时,头结点的位置为0
	 * @param item 表示插入结点的数据域
	 * @return 插入成功返回true;否则返回false
	 */
	public boolean insertItem(int pos, Object item) {
		if(pos < 0 || pos > length) {
			return false;
		}
		else {
			Node p = new Node(item);
			if(pos == 0) {
				p.setLeftLink(headNode);
				p.setRightLink(headNode.getRightLink());
				headNode.getRightLink().setLeftLink(p);
				headNode.setRightLink(p);
			} else if (pos == length) {
				p.setLeftLink(headNode.getLeftLink());
				p.setRightLink(headNode);
				headNode.getLeftLink().setRightLink(p);
				headNode.setLeftLink(p);
			} else {
				int count = 1;
				Node r = headNode.getRightLink();
				while(count < pos) {
					r = r.getRightLink();
					count++;
				}
				p.setLeftLink(r);
				p.setRightLink(r.getRightLink());
				r.getRightLink().setLeftLink(p);
				r.setRightLink(p);
			}
			return true;
		}
	}

	/**
	 * 删除带头结点的双向链表中第一次出现数据域为item的结点
	 *
	 * @param item 删除结点的数据域
	 * @return 删除成功,返回true;否则,返回false
	 */
	public boolean deleteItem(Object item) {
		Node p = headNode.getRightLink(), r = headNode;
		while(p != headNode) {
			if(p.getData() == item) {
				r.setRightLink(p.getRightLink());
				p.getRightLink().setLeftLink(r);
				return true;
			}
			else {
				r = p;
				p = p.getRightLink();
			}
		}
		return false;
	}
}

数据结构之双向链表(包含双向循环链表)

时间: 2024-08-25 17:14:46

数据结构之双向链表(包含双向循环链表)的相关文章

数据结构开发(11):双向循环链表的实现

0.目录 1.双向循环链表的实现 2.小结 1.双向循环链表的实现 本节目标: 使用 Linux 内核链表实现 StLib 中的双向循环链表 template <typename T> class DualCircleList; StLib 中双向循环链表的设计思路: 数据结点之间在逻辑上构成双向循环链表,头结点仅用于结点的定位. 实现思路: 通过模板定义 DualCircleList 类,继承自 DualLinkList 类 在 DualCircleList 内部使用Linux内核链表进行实

1.Go-copy函数、sort排序、双向链表、list操作和双向循环链表

1.1.copy函数 通过copy函数可以把一个切片内容复制到另一个切片中 (1)把长切片拷贝到短切片中 package main import "fmt" func main() { s1 := []int {1,2} s2 := []int{3,4,5,6} //copy的是角标,不会增加元切片的长度 copy(s1,s2) fmt.Println(s1) //[3 4] fmt.Println(s2) //[3 4 5 6] } (2)把短切片拷贝到长切片中 package ma

数据结构8: 双向链表(双向循环链表)的建立及C语言实现

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

小猪的数据结构辅助教程——2.7 线性表中的双向循环链表

小猪的数据结构辅助教程--2.7 线性表中的双向循环链表 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点: 1.了解引入双向循环链表的原因 2.熟悉双向循环链表的特点以及存储结构 3.掌握双向循环链表的一些基本操作的实现逻辑 4.掌握逆序输出双向循环链表元素逻辑 1.双向循环链表的引入 2.双向循环链表的存储结构 双向循环链表的特点: 上面也说了,空间换时间,比起循环链表只是多了一个指向前驱的指针 特点的话: 判断空表:L ->next = L -> prior = L; 存

数据结构-双向循环链表(无头结点)相关算法

#include <stdio.h>#include <stdlib.h>#define OVERFLOW -2#define OK 1#define ERROR 0 //此双向循环链表无头结点typedef int ElemType;typedef struct DulNode {    ElemType data;    struct DulNode *prior;    struct DulNode *next;}DulNode,*DulLinkList; DulLinkLi

数据结构基础(12) --双向循环链表的设计与实现

双向链表的操作特点: (1) "查询" 和单链表相同; (2)"插入" 和"删除"时需要同时修改两个方向上的指针. 但是对于双向循环链表则在表尾插入非常的迅速, 只需O(1)的时间,因为有指向前面的指针, 因此双向循环链表会很容易的找到位于表尾的元素,因此双向循环链表比较适用于频繁在表尾插入的情况. 空链表: 双向循环链表节点构造: class DoubleListNode { private: Type data; DoubleListNode

数据结构_线性表_链式存储_双向循环链表的基本操作

//双向链表,将头结点和尾结点链接起来,就构成了双向循环链表 //双向循环链表是将头结点的前驱指针指向了尾结点,同时将尾结点的后劲指针指向了头结点. //空表,头结点的前驱和后继指针均指向了自己,这也是判断双向循环链表是否为空的条件, //双向循环链表具有对称性 //缺点,是要付出空间代价的 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点.一般我们都构造双向循环链表. 代

数据结构与算法——线性表链式存储(双向循环链表)

今天总结线性表中的双向循环链表. 什么是双向循环链表? 看名字估计也就知道了,首相他是一个循环链表,也就是最后一个结点的指针域不为空,而是指向头结点,其次与单向循环链表相比,它是双向的.所谓双向,就是给每个结点再增加一个指针域,这个指针域指向前一个结点. 即是下面这样(来自百度图片): 为什么要用双向循环链表? 无论单链表还是单向循环链表,都只有一个指针域,它们都是直接指向后继结点的,如果要查找当前结点的后继结点,会很方便.但是如果给定一个结点,要得到它的前继结点,就会很麻烦,必须从第一个元素开

数据结构_双向循环链表_tostring方法

//双向循环链表 class DoubleNode { constructor (data) { this.data = data this.prev = null this.next = null } } class DoubleCycleList { constructor () { this.head = null this.tail = null this.length = 0 } //追加数据 append (data) { let newNode = new DoubleNode(d