You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
|
|
|
|
为什么引入双链表?单链表的结点中只有一个指向其后继的指针,使得单链表要访问某个结点的前驱结点时,只能从头开始遍历,访问后驱结点的复杂度为O(1),访问前驱结点的复杂度为O(n)。为了克服上述缺点,引入了双链表。
|
|
|
|
|
单向链表查找的方向只能是一个方向,而双向链表可以向前或者向后查找;单链表如果想要实现删除操作,需要找到待删除节点的前一个节点。而双向链表可以实现自我删除。
|
|
|
|
|
|
|
|
|
|
![[104421_43592.png]]
|
|
|
|
|
|
|
|
|
|
![[104543_51020.png]]
|
|
|
|
|
|
|
|
|
|
双链表的遍历与单链表相同,可以从头或者从尾反向遍历,参考java LinkedList源码可以实现先判断下标处于的位置来选择从头或者尾遍历提高性能
|
|
|
|
|
|
|
|
|
|
以下是双链表部分实现
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
public class DoubleLinkList<T> {
|
|
|
|
|
/**
|
|
|
|
|
* 头节点
|
|
|
|
|
*/
|
|
|
|
|
private Node<T> first;
|
|
|
|
|
/**
|
|
|
|
|
* 尾节点
|
|
|
|
|
*/
|
|
|
|
|
private Node<T> tail;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 元素数量
|
|
|
|
|
*/
|
|
|
|
|
private int size;
|
|
|
|
|
|
|
|
|
|
public DoubleLinkList() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 头插法
|
|
|
|
|
*
|
|
|
|
|
* @param data
|
|
|
|
|
*/
|
|
|
|
|
public void addFirst(T data) {
|
|
|
|
|
Node<T> newNode = new Node<>(data);
|
|
|
|
|
if (size == 0) {
|
|
|
|
|
//只有一个元素时,头、尾都是同一个
|
|
|
|
|
first = newNode;
|
|
|
|
|
tail = newNode;
|
|
|
|
|
} else {
|
|
|
|
|
newNode.next = first;
|
|
|
|
|
first.prev = newNode;
|
|
|
|
|
first = newNode;
|
|
|
|
|
}
|
|
|
|
|
size++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 尾插法
|
|
|
|
|
*
|
|
|
|
|
* @param data
|
|
|
|
|
*/
|
|
|
|
|
public void addLast(T data) {
|
|
|
|
|
if (size == 0) {
|
|
|
|
|
addFirst(data);
|
|
|
|
|
} else {
|
|
|
|
|
Node<T> newNode = new Node<>(data);
|
|
|
|
|
newNode.prev = tail;
|
|
|
|
|
tail.next = newNode;
|
|
|
|
|
tail = newNode;
|
|
|
|
|
size++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 插入指定位置
|
|
|
|
|
*
|
|
|
|
|
* @param data
|
|
|
|
|
* @param index
|
|
|
|
|
*/
|
|
|
|
|
public void insert(T data, int index) {
|
|
|
|
|
checkIndex(index);
|
|
|
|
|
if (index == 0) {
|
|
|
|
|
addFirst(data);
|
|
|
|
|
} else if (index == size) {
|
|
|
|
|
addLast(data);
|
|
|
|
|
} else {
|
|
|
|
|
Node<T> newNode = new Node<>(data);
|
|
|
|
|
//从头遍历,可以判断index实现从头或尾遍历
|
|
|
|
|
Node<T> head = first;
|
|
|
|
|
for (int i = 0; i < index - 1; i++) {
|
|
|
|
|
head = head.next;
|
|
|
|
|
}
|
|
|
|
|
newNode.next = head.next;
|
|
|
|
|
head.next = newNode;
|
|
|
|
|
newNode.prev = head;
|
|
|
|
|
newNode.next.prev = newNode;
|
|
|
|
|
size++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据下标获取元素
|
|
|
|
|
*
|
|
|
|
|
* @param index
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public T getByIndex(int index) {
|
|
|
|
|
checkIndex(index);
|
|
|
|
|
Node<T> node = first;
|
|
|
|
|
for (int i = 0; i < index; i++) {
|
|
|
|
|
node = node.next;
|
|
|
|
|
}
|
|
|
|
|
return node.element;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 打印输出
|
|
|
|
|
*/
|
|
|
|
|
public void display() {
|
|
|
|
|
Node<T> node = first;
|
|
|
|
|
System.out.println(node.element);
|
|
|
|
|
for (int i = 0; i < size - 1; i++) {
|
|
|
|
|
node = node.next;
|
|
|
|
|
System.out.println(node.element);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 计算元素数量
|
|
|
|
|
*
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public int sizeOf() {
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 校验下标
|
|
|
|
|
*
|
|
|
|
|
* @param index
|
|
|
|
|
*/
|
|
|
|
|
private void checkIndex(int index) {
|
|
|
|
|
if (index < 0 || index > size) {
|
|
|
|
|
throw new IndexOutOfBoundsException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 链表节点对象
|
|
|
|
|
*
|
|
|
|
|
* @param <T>
|
|
|
|
|
*/
|
|
|
|
|
private static class Node<T> {
|
|
|
|
|
/**
|
|
|
|
|
* 元素
|
|
|
|
|
*/
|
|
|
|
|
T element;
|
|
|
|
|
/**
|
|
|
|
|
* 后指针
|
|
|
|
|
*/
|
|
|
|
|
Node<T> next;
|
|
|
|
|
/**
|
|
|
|
|
* 前指针
|
|
|
|
|
*/
|
|
|
|
|
Node<T> prev;
|
|
|
|
|
|
|
|
|
|
public Node(T element) {
|
|
|
|
|
this.element = element;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|