数据结构-双向链表

为了解决对链表操作的灵活性,把单链表的单一指向改为双向驱动,从而形成双向链表。Java的LinkedList就是双向链表的实现,但是因为有双端队列的成分,显得有些不单纯。

■双向链表的节点定义

private static class Node<E> {

E item;

Node<E> next;

Node<E> prev;

Node(Node<E> prev, E element, Node<E> next) {

this.item = element;

this.next = next;

this.prev = prev;

}

}

这一节点定义的变化提高了对链表操作的性能,下面举一个例子:

/**

* Inserts element e before non-null Node succ.

*/

void linkBefore(E e, Node<E> succ) {

// assert succ != null;

final Node<E> pred = succ.prev;

final Node<E> newNode = new Node<>(pred, e, succ);

succ.prev = newNode;

if (pred == null)

first = newNode;

else

pred.next = newNode;

size++;

modCount++;

}

因为相邻前节点的信息已经被存储,可以直接通过这一信息获得前一个数据元素,而不是像单链表那样每次都从头再来。虽然花费了内存,但是提高了性能,从而节约了时间。有时候内存和性能是个平衡点的问题。

■在这里,就不整篇幅的列举代码了,以下亮点特别说明一下。

二分查找的应用:

/**

* Returns the (non-null) Node at the specified element index.

*/

Node<E> node(int index) {

// assert isElementIndex(index);

if (index < (size >> 1)) {

Node<E> x = first;

for (int i = 0; i < index; i++)

x = x.next;

return x;

} else {

Node<E> x = last;

for (int i = size - 1; i > index; i--)

x = x.prev;

return x;

}

}

接近前端从前开始,接近后端从后开始,比单链表显得“工业化”了些。

在1.8版增加了spliterator处理,在将来spliterator会有很广泛的应用的。

/**

* Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>

* and <em>fail-fast</em> {@link Spliterator} over the elements in this

* list.

*

* <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and

* {@link Spliterator#ORDERED}.  Overriding implementations should document

* the reporting of additional characteristic values.

*

* @implNote

* The {@code Spliterator} additionally reports {@link Spliterator#SUBSIZED}

* and implements {@code trySplit} to permit limited parallelism..

*

* @return a {@code Spliterator} over the elements in this list

* @since 1.8

*/

@Override

public Spliterator<E> spliterator() {

return new LLSpliterator<E>(this, -1, 0);

}

/** A customized variant of Spliterators.IteratorSpliterator */

static final class LLSpliterator<E> implements Spliterator<E> {

static final int BATCH_UNIT = 1 << 10;  // batch array size increment

static final int MAX_BATCH = 1 << 25;  // max batch array size;

final LinkedList<E> list; // null OK unless traversed

Node<E> current;      // current node; null until initialized

int est;              // size estimate; -1 until first needed

int expectedModCount; // initialized when est set

int batch;            // batch size for splits

LLSpliterator(LinkedList<E> list, int est, int expectedModCount) {

this.list = list;

this.est = est;

this.expectedModCount = expectedModCount;

}

final int getEst() {

int s; // force initialization

final LinkedList<E> lst;

if ((s = est) < 0) {

if ((lst = list) == null)

s = est = 0;

else {

expectedModCount = lst.modCount;

current = lst.first;

s = est = lst.size;

}

}

return s;

}

public long estimateSize() { return (long) getEst(); }

public Spliterator<E> trySplit() {

Node<E> p;

int s = getEst();

if (s > 1 && (p = current) != null) {

int n = batch + BATCH_UNIT;

if (n > s)

n = s;

if (n > MAX_BATCH)

n = MAX_BATCH;

Object[] a = new Object[n];

int j = 0;

do { a[j++] = p.item; } while ((p = p.next) != null && j < n);

current = p;

batch = j;

est = s - j;

return Spliterators.spliterator(a, 0, j, Spliterator.ORDERED);

}

return null;

}

public void forEachRemaining(Consumer<? super E> action) {

Node<E> p; int n;

if (action == null) throw new NullPointerException();

if ((n = getEst()) > 0 && (p = current) != null) {

current = null;

est = 0;

do {

E e = p.item;

p = p.next;

action.accept(e);

} while (p != null && --n > 0);

}

if (list.modCount != expectedModCount)

throw new ConcurrentModificationException();

}

public boolean tryAdvance(Consumer<? super E> action) {

Node<E> p;

if (action == null) throw new NullPointerException();

if (getEst() > 0 && (p = current) != null) {

--est;

E e = p.item;

current = p.next;

action.accept(e);

if (list.modCount != expectedModCount)

throw new ConcurrentModificationException();

return true;

}

return false;

}

public int characteristics() {

return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;

}

}

■ArrayList和LinkedList的区别

ArrayList

实现了List接口

数据元素在内部用数组保存

指定索引的get/set速度很快

插入和删除数据元素在某些场合要花点时间

LinkedList

除了List接口外,还实现了双端队列,是双向链表

虽然指定索引的get/set不太灵光,但插入和删除数据元素要比ArrayList表现的要好些。

时间: 2024-10-12 23:23:32

数据结构-双向链表的相关文章

数据结构——双向链表的实现

双向链表主要为了解决单链表找前驱的问题.除了插入.删除操作之外,其他操作与单链表都相同.因此这里只是比较简单的写了双向链表的插入和删除操作.画出结点结构图的话,思路会很清晰,线性表这块还算是比较简单的能够实现. 1 /* 2 在单链表中,求后继的方法NextElem执行的时间为O(1),求前驱的方法PriorElem执行的时间为O(n), 3 引入双向链表就是为了克服单链表这种单向性的缺点. 4 */ 5 6 #include<stdio.h> 7 #include<stdlib.h&g

数据结构 双向链表 C语言实现

dlist.h 1 #ifndef __dList_H 2 #define __dlist_H 3 4 typedef int Item; 5 typedef struct Node *PNode; 6 typedef PNode Position; 7 /*定义节点类型*/ 8 typedef struct Node 9 { 10 Item data; /*数据域*/ 11 PNode previous; /*指向前驱*/ 12 PNode next; /*指向后继*/ 13 }Node; 1

Linux内核分析--内核中的数据结构双向链表续【转】

在解释完内核中的链表基本知识以后,下面解释链表的重要接口操作: 1. 声明和初始化 实际上Linux只定义了链表节点,并没有专门定义链表头,那么一个链表结构是如何建立起来的呢?让我们来看看LIST_HEAD()这个宏: #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) 需要注意的是,Linux 的每个双循

喜羊羊系列之数据结构双向链表

博客:http://blog.csdn.net/muyang_ren 关于双向链表的原理很多都有说明,我这只是与前面的内核链表作个对比,同样实现数据的增删差改. 截图: 1.main.c #include "doublelist.h" int main(void) { int num, i; double_plist list; doublelist_init(&list); //初始化双向链表 printf("\n请输入链表长度:"); scanf(&qu

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

// ------DoublyLinkedList.h------ template <class T> class DNode { private: // 指向左.右结点的指针 DNode<T> * left; DNode<T> * right; public: // data为公有成员 T data; // 构造函数 DNode(void); DNode(const T& item); // 改变表的方法 void InsertRight(DNode<

C实现通用数据结构--双向链表

双向链表概述 双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继next和直接前驱prev.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点.为了标识链表的头和尾,将第一个元素的prev指针和最后一个元素的next指针设置为NULL 要反向遍历整个双向链表,使用prev指针从尾到头的顺序访问各个元素,因此为每个元素增加了一个指针的代价,换来的是双向链表更加灵活的访问. 本文地址:http://www.cnblogs.com/archi

Python数据结构--双向链表

1 ''' 2 双向链表包含第一个和最后一个的链接元素. 3 每个链接都有一个数据字段和两个称为next和prev的链接字段. 4 每个链接都使用其下一个链接与其下一个链接链接. 5 每个链接都使用其上一个链接与之前的链接链接. 6 最后一个链接将链接作为空来标记列表的结尾. 7 ''' 8 9 10 # 创建节点 11 class Node(): 12 def __init__(self, data): 13 self.data = data 14 self.next = None 15 se

Java数据结构——双向链表

什么是双向链表?每一个结点不仅配有next引用,同时还有一个prev引用,指向其上一个结点(前驱结点), 没有前驱的时候就为NULL. (以下图片均来自网络,侵删) 与单链表的区别?和单向链表相比有以下优势: 插入删除不需要移动元素外,可以原地插入删除 可以双向遍历 插入操作 删除操作 实现 public class DLNode { Object data; DLNode prev; DLNode next; static DLNode head; static DLNode tail; //

算法导论第十章数据结构--双向链表

看的概念挺朦胧的,没有明确好双链表到底需要哪些方法,其实针对这种结构应该可以写很多方法,并没有什么特定标准. 不过真是只看不练不行啊,一下手各种错误,各种溢出 #include<iostream> using namespace std; template<class T> struct Node { T value; Node<T>* pre; Node<T>* next; }; template<class T> class Flist {