一. 雙向鏈表的特點
- 每個節點維護了prev,next 兩個節點和 元素e
- 不僅可以像單鏈表向後查找,還可以向前查找。
- 添加/刪除第一個節點和最後一個節點一個節點的時間複雜度都是O(1)
- 查找的時候,可以判斷index 在前半部分還是後半部分,查找節點。
二.雙向鏈表的插入和刪除
插入節點:首先找到要插入節點newNode的位置,並且找到前一個節點prevNode,和後一個節點nextNode, prevNode的next 指向新的節點,newNode的prev 指prevNode,newNode節點next指向nextNode,nextNode的prev指向newNode 示意圖如下:
刪除節點:找到刪除delNode的節點的位置,並且找到前一個節點prevNode,和後一個節點nextNode, prevNode的next 指向nextNode,newNode的prev 指prevNode ,如下圖:
三. 雙向鏈表時間複雜度分析
- 刪除/新增 第一個和最後一個元素的時間複雜度爲O(1)
- 刪除/新增 元素的時間複雜度爲O(n)
四.Java 雙向鏈表的簡單實現
/**
* @author 一直往前走
* @date 2019/08/02
*/
public class DoubleList<E> {
public Node<E> first;
public Node<E> last;
public int size;
public int modCount;
public DoubleList() {
}
/**
* 添加第一個元素
*
* @param e
*/
public void addFirst(E e) {
final Node f = first;
final Node newNode = new Node<E>(null, e, f);
if (f == null) {
last = newNode;//newNode賦值last
} else {
first.prev = newNode; //newNode賦值給first.prev
}
newNode.next = first;
first = newNode;
size++;
modCount++;
}
/**
* 添加最後一個元素
*
* @param e
*/
public void addLast(E e) {
final Node l = last;
final Node newNode = new Node(l, e, null);
if (l == null) {
first = newNode;
} else {
last.next = newNode;
}
newNode.prev = last;
last = newNode;
size++;
modCount++;
}
public void add(int index, E e) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("Add failed. index illegal");
}
if (index == 0) {
addFirst(e);
} else if (index == size) {
addLast(e);
} else {
addBefore(e, node(index));
}
}
/**
* 根據index位置獲取節點
* 利用雙向鏈表的特點提高查找效率
*
* @param index
* @return
*/
private Node<E> node(int 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;
}
}
/**
* 在節點node前添加元素e
*
* @param e
* @param node
*/
public void addBefore(E e, Node node) {
Node prev = node.prev;
Node newNode = new Node(prev, e, node);
if (prev == null) {
first = newNode;
} else {
prev.next = newNode;
}
node.prev = newNode;
size++;
modCount++;
}
/**
* 在節點node後添加元素e
*
* @param e
* @param node
*/
public void addAfter(E e, Node node) {
Node next = node.next;
Node newNode = new Node(node, e, next);
if (next == null) {
last = newNode;
} else {
next.prev = newNode;
}
node.next = newNode;
size++;
modCount++;
}
/**
* 雙鏈表
*
* @param <E>
*/
class Node<E> {
public E e;
public Node next, prev;
public Node() {
e = null;
next = null;
prev = null;
}
public Node(Node prev, E e, Node next) {
this.e = e;
this.prev = prev;
this.next = next;
}
public String toString() {
StringBuilder sb = new StringBuilder();
Node cur = this;
while (cur != null) {
sb.append(cur.e + "->");
cur = cur.next;
}
sb.append("NULL");
return sb.toString();
}
}
public static void main(String[] args) {
DoubleList<Integer> doubleList = new DoubleList();
for (int i = 0; i < 10; i++) {
doubleList.addFirst(i);
System.out.println(doubleList);
}
doubleList.add(1, 2);
System.out.println(doubleList);
}
}