`

双向链表之我的理解

 
阅读更多
谈到数据结构性能, 时常被问到ListLink的区别, 在回答之后,不可能避免的会将问题的焦点引到这两种存储方式的底层实现上来. List较为简洁,线形存储即可,Link基本结构, 则是考察的重点.例如说,双向链表.Java JDK中的java.util.LinkedList就是一个双链表。

 

双向链表的结构如下,每个节点包括3个属性(前节点引用,后节点引用,数值)。这两个引用,指向它的前后的节点,这样就克服普通链表只能向后遍历的局限: (自己画的图真丑!!!)

 

来张网上的截图,H为头节点,valuenull,它的引用分别指向最前和最后的两个节点。这样从两头可以遍历整个链表。

[疑问]Java数据结构和算法(第二版)中,第一个节点的prev和最后一个节点的next指向的null,而网上的例子都指向head节点,望看到的回复下,谢谢了。


双向链表的插入:

双向链表的删除:

插入和删除是链表的优势所在,只需要改变4个引用的指向即可。而List则需移动改节点后面的所有元素,相对效率差了很多。

 

参考了网上很多代码实现,结合Java的面向对象的特点,整理了一份源码,如下:

 

文件一: MyList 接口, 仅一个实现类的情况下,可无视.

/**
 * 仅添加几个主要的功能方法.
 * size,isEmpty较为简单,未考虑
 * @author 完美天龙
 *
 */
public interface MyList {
	public void add(Object o);
	public void delete(int i);
	public void addLast(Object o);
	public void addFirst(Object o);
	public Object get(int i);
}
文件二:功能的实现类, 实现了MyList接口, 并拥有自己的具体实现方法.我加了很多注释,只要自己用心去看.肯定能理解.

 

public class MyDoubleLinkedList implements MyList {
	private Node head = new Node(null);
	private int size = 0;

	/**
	 * 新节点加添,默认至链表最尾段, 也就是head的前面.
	 * 
	 * @param o
	 */
	public void add(Object o) {
		addBefore(new Node(o), head);
	}

	/**
	 * 删除指定的节点
	 * 
	 * @param o
	 */
	public void delete(int i) {
		removeNode(getNodeByIndex(i));

	}

	/**
	 * 查询指定位置的节点的value
	 * 
	 * @param i
	 * @return
	 */
	public Object get(int i) {
		return getNodeByIndex(i).getValue();
	}

	/**
	 * 添加节点至链表的尾端,即添加至head之前.
	 * 
	 * @param o
	 */
	public void addLast(Object o) {
		addBefore(new Node(o), head);
	}

	/**
	 * 添加节点至链表的首位,即添加至head之后.
	 * 
	 * @param o
	 */
	public void addFirst(Object o) {
		addAfter(new Node(o), head);
	}

	/**
	 * 将新节点插入至已知的节点的前一个.
	 * 
	 * @param newNode
	 *            待添加的节点
	 * @param node
	 *            指定已知的节点
	 */
	private void addBefore(Node newNode, Node node) {
		// TODO Auto-generated method stub
		newNode.setNext(node);
		newNode.setPrev(node.getPrev());
		newNode.getNext().setPrev(newNode);
		newNode.getPrev().setNext(newNode);
		size++;
	}

	/**
	 * 将新节点插入至已知的节点的后一个.
	 * 
	 * @param newNode
	 *            待添加的节点
	 * @param node
	 *            指定已知的节点
	 */
	private void addAfter(Node newNode, Node node) {
		newNode.setPrev(node);
		newNode.setNext(node.getNext());
		newNode.getPrev().setNext(newNode);
		newNode.getNext().setPrev(newNode);
		size++;
	}

	/**
	 * 链表打印,默认以next方式向后遍历 当然,我们也可以向前prev遍历,国际惯例是next
	 */
	public String toString() {
		StringBuffer ss = new StringBuffer("[");
		Node node = head;
		for (int i = 0; i < size; i++) {
			node = node.getNext();
			if (i > 0) {
				ss.append(",");
			}
			ss.append(node.getValue().toString());
		}
		ss.append("]");
		return ss.toString();
	}

	/**
	 * 添加节点至指点索引处,需要如下两部操作: 1> 找到该索引处的node. 2> 将新节点插入到所返回的node之后.
	 * 
	 * @param i
	 *            指定索引
	 * @param o
	 *            新节点
	 * @return boolean 返回值表示是否插入成功
	 */

	public boolean add(int i, Object o) {
		addBefore(new Node(o), getNodeByIndex(i));
		return true;
	}

	/**
	 * 这个的i,应该理解为链表的第几个节点,从1开始算,而不是直接的直接的索引.所以第i个元素的索引应该为i-1.
	 * 从head节点开始,不断向后遍历,找到i-1对应的节点.
	 * 
	 * @param i
	 *            第i-1个节点.
	 * @return 返回i-1位置的Node.
	 */
	private Node getNodeByIndex(int index) {
		Node node = head;
		if (index < 0 || index > size) {// 位置非法则抛出索引越界异常
			throw new IndexOutOfBoundsException();
		} else {
			for (int j = 0; j < index; j++) {
				node = node.getNext();
			}
		}
		return node;
	}

	/**
	 * 删除指定值为value的节点. 1> 找到该节点在链表中的位置. 2> 更新该节点前后节点的指向,并将自己的前后指向设置为null.
	 * 
	 * @param node
	 */
	private void removeNode(Node node) {
		node.getPrev().setNext(node.getNext());
		node.getNext().setPrev(node.getPrev());
		node.setNext(null);
		node.setPrev(null);
		size--;
	}
	/**
	 * @return the head
	 */
	public Node getHead() {
		return head;
	}
	/**
	 * @param head
	 *            the head to set
	 */
	public void setHead(Node head) {
		this.head = head;
	}
	/**
	 * @return the size
	 */
	public int getSize() {
		return size;
	}
	/**
	 * @param size
	 *            the size to set
	 */
	public void setSize(int size) {
		this.size = size;
	}

}

 

文件三:测试类。模仿的网上的例子,受网络限制,我找不到原出处了,So Sorry!

public class MyDoubleLinkedListTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		MyDoubleLinkedList dll = new MyDoubleLinkedList();
		
		//1. 给链表添加节点.
		dll.add("乔峰");
		dll.add("段誉");
		dll.add("虚竹");
		dll.add("慕容复");		
		System.out.println(dll);
		
		//2. 添加节点至链表尾端.
		dll.addLast("天山童姥");
		System.out.println(dll);
		
		//3. 添加节点至链表头部.
		dll.addFirst("扫地僧");
		System.out.println(dll);
		
		//4. 添加节点至指定位置:如"王雨嫣"插入至队列第4个.
		dll.add(4,"王语嫣");
		System.out.println(dll);
		
		//5. 删除指定位置的节点,如 删除第4个节点,即刚才添加的"王语嫣".
		dll.delete(4);
		System.out.println(dll);
		
		//6. 查询指定位置的节点数值.
		System.out.println(dll.get(4));
		
	}

}

 测试类执行结果:

[乔峰,段誉,虚竹,慕容复]

[乔峰,段誉,虚竹,慕容复,天山童姥]

[扫地僧,乔峰,段誉,虚竹,慕容复,天山童姥]

[扫地僧,乔峰,段誉,王语嫣,虚竹,慕容复,天山童姥]

[扫地僧,乔峰,段誉,虚竹,慕容复,天山童姥]

虚竹

 

基本功能模拟成功.

 

  • 大小: 3.1 KB
  • 大小: 23.4 KB
  • 大小: 90.2 KB
  • 大小: 12.9 KB
  • 大小: 11.7 KB
分享到:
评论

相关推荐

    支持类模版的C++双向链表

    在C++编程中,双向链表是一种非常重要的数据结构,它允许在列表的任一位置进行插入和删除操作,而不必像数组那样依赖于索引。在这个“支持类模版的C++双向链表”中,我们将探讨如何利用C++的模板特性来实现一个灵活...

    Linux内核双向链表简单分析

    ### Linux内核双向链表简单分析 #### 链表数据结构简介 链表作为一种基本且重要的数据结构,在...通过理解这些基本概念和技术细节,开发者可以更高效地利用双向链表来解决实际问题,并更好地参与到Linux内核开发中。

    数据结构-双向链表

    理解并熟练掌握双向链表的原理和操作,对于提升编程能力,特别是数据结构和算法的理解,有着极大的帮助。 总之,双向链表是一种强大的数据结构,它的设计使得在链表中进行插入、删除和查找等操作更为灵活。通过理解...

    java 单链表和双向链表的实现

    首先,我们来理解单链表和双向链表的基本概念。单链表是一种线性数据结构,其中每个节点包含数据和一个指向下一个节点的引用。而双向链表则更进一步,每个节点不仅有指向下一个节点的指针,还有一个指向前一个节点的...

    操作系统课设-线程安全的双向链表

    首先,我们需要理解双向链表的基本概念。双向链表是一种数据结构,每个节点包含两个指针,分别指向它的前一个节点和后一个节点。与单链表相比,双向链表允许我们在正向和反向两个方向上高效地遍历数据,但同时也增加...

    双向链表双向链表双向链表

    双向链表是一种高级数据结构,它在计算机科学中被广泛应用于各种算法和...理解并熟练掌握其原理和操作方法是每一个IT专业人士的基础技能之一。通过实践和应用,我们可以更好地利用双向链表的优势来优化我们的程序设计。

    C++双向链表统计文章单词出现频率

    在这个特定的项目中,“C++双向链表统计文章单词出现频率”是一个涉及数据结构和算法的应用,目标是实现一个程序来分析文本文件,计算并显示文章中每个单词出现的次数。双向链表作为数据结构的核心,其特点是每个...

    stm32f103 双向链表

    在这个项目中,我们讨论的是如何在STM32F103上实现一个双向链表的数据结构。双向链表是一种特殊的数据结构,它允许我们在列表中的节点之间进行前向和后向的移动,这在处理动态数据集合时非常有用。 首先,我们要...

    数据结构 双向链表(C++)

    在IT领域,数据结构是计算机科学的基础,它们是组织、存储和处理数据的方式。双向链表是一种特殊的数据结构,它在计算机程序设计中...理解并熟练掌握双向链表的原理和C++实现对于提升编程技能和解决实际问题至关重要。

    数据结构之双向链表

    双向链表是一种重要的线性数据结构,与常见的单向链表相比,它具有更丰富的操作能力。本文将深入探讨双向链表的概念、特点、实现方式以及在给定的“DoubleLink”压缩包中可能包含的相关功能。 双向链表(Doubly ...

    C++经典算法 双向链表

    ### C++经典算法:双向链表 在计算机科学领域中,数据结构是极其重要的基础知识之一。其中链表作为一种常见的线性表实现方式,在各种应用场景中都有广泛的应用。本篇文章将根据给定的代码片段深入探讨双向链表的...

    大数阶乘 双向链表

    本主题聚焦于如何使用双向链表这一数据结构来实现大数阶乘的计算。双向链表允许我们有效地存储和操作大数,同时保持良好的性能。 首先,我们需要了解双向链表的基本概念。双向链表是一种线性数据结构,其中每个节点...

    双向链表的C++实现

    双向链表是一种重要的线性数据结构,与单链表不同,它在每个节点中都包含两个指针,一个指向前一个节点,另一个指向后一个节点。这种设计允许双向遍历,使得插入和删除操作更加灵活。在本主题中,我们将深入探讨如何...

    链存储结构-双向链表

    同时,作者提到的个人思路和总结,可能是对实现过程中遇到的问题、优化方案或者设计模式的探讨,对于提升对双向链表的理解和应用能力非常有帮助。 总的来说,链存储结构中的双向链表是一种重要的数据结构,它在许多...

    双向链表的增删改查

    在计算机科学中,数据结构是组织和管理数据的重要方式,而链表作为一种基本的数据结构,被广泛用于...通过理解和实现这些基本操作,开发者可以熟练掌握双向链表的数据结构,这对于理解更复杂的算法和数据结构至关重要。

    用双向链表做的n的阶乘

    首先,我们要理解双向链表的基本结构。每个节点包含三个部分:数据(data)、指向前一个节点的指针(prev)和指向后一个节点的指针(next)。创建双向链表的第一步是创建一个头节点,通常不存储任何数据,仅作为链表...

    网上搜集的七种双向链表模板c++实现

    在编程领域,双向链表是一种重要的数据结构,它在C++中被广泛使用。与单向链表不同,双向链表允许元素在正向和反向两个方向上进行遍历,这为程序员提供了更大的灵活性。这里我们将深入探讨网上搜集的七种双向链表...

    双向链表的实现

    总的来说,双向链表是C++中重要的数据结构之一,它的理解和应用对于提升编程技能和解决实际问题具有重要意义。通过手动实现双向链表,你可以深入了解数据结构的工作原理,同时也能更好地掌握C++的指针和内存管理。

    数据结构双向链表

    但不同之处在于,双向链表的节点还包含一个指向前一个节点的指针。这种设计使得在链表中向前和向后移动都变得容易。 1. **基本概念** 双向链表的每个节点由三部分组成:数据、前向指针(forward pointer)和后向...

Global site tag (gtag.js) - Google Analytics