在实现了单向链表后,我们在使用单向链表中会发现一个问题:在单向链表中查找某一个结点的下一个结点的时间复杂度是O(1),但是查找这个结点的上一个结点的时候,时间复杂度的最大值就变成了O(n),因为在查找这个指定结点的上一个结点时又需要从头开始遍历。
那么该如何解决这个困难呢?我们可以在单链表的每个结点中,再设置一个指向前驱结点的指针域,这就构成了双向链表。在双向链表中含有两个指针域,一个指向直接前驱,一个指向直接后继。
双向链表是单向链表的拓展,所以程序大致与与单向链表的程序相似。
这里需要注意的是结点的插入和删除步骤:
将结点s插入到结点p和p->next之间时
(1)将s前驱指向结点p,如图中①
(2)将s的后继指向结点p->next,如图中②
(3)将p->next的前驱指向结点s,如图中③
(4)将p的后继指向结点s,如图中④
将结点p删除
(1)将p->prior结点的后驱指针指向p->next结点
(2)将p->next结点的前驱指针指向p->prior结点
(3)解除p结点和其前驱后继的关系
上代码
package likend;
/**
* Created by yxf on 2018/4/5.
* 双向链表
*/
public class DoubleLink<T> {
private Node header; //链表头结点
private Node tail; //链表尾结点
private int size; //保存已经有的结点
public class Node<T> {
private T data; //数据
private Node prev; //指向上一个节点的引用
private Node next; //指向下一个节点的引用
public Node() {
}
public Node(T data, Node prev, Node next) {
this.data = data;
this.prev = prev;
this.next = next;
}
}
public DoubleLink() {
}
/**
* 在尾部添加
*
* @param element
* @return
*/
public boolean add(T element) {
linkLast(element);
return true;
}
/**
* 获取指定索引处的元素
*
* @param index
* @return
*/
public T getElement(int index) {
return (T) getNodeByIndex(index).data;
}
/**
* 获取指定位置的结点
*
* @param index
* @return
*/
public Node getNodeByIndex(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("获取位置超过了链表长度范围");
Node currentNode = header;
for (int i = 0; i < index; i++) {
currentNode = currentNode.next;
}
return currentNode;
}
/**
* 获取指定位置前驱的结点
*
* @param index
* @return
*/
public Node getNodeByIndexBefore(int index) {
Node preNode = header;
for (int i = 0; i < index - 1; i++) {
preNode = preNode.next; //获得前驱结点
}
return preNode;
}
/**
* 获取指定元素的前驱
*
* @param currentElem
* @return
*/
public T priorElement(T currentElem) {
int index = getIndex(currentElem);
if (index == -1)
return null;
else {
if (index == 0) {
return null;
} else {
return (T) getNodeByIndex(index - 1).data;
}
}
}
/**
* 获取指定元素的后驱
*
* @param currentElem
* @return
*/
public T nextElement(T currentElem) {
int index = getIndex(currentElem);
if (index == -1)
return null;
else {
if (index == size - 1) {
return null;
} else {
return (T) getNodeByIndex(index + 1).data;
}
}
}
//查询链表中指定元素所在的位置
public int getIndex(T element) {
Node current = header;
for (int i = 0; i < size && current != null; i++, current = current.next) {
if (current.data.equals(element))
return i;
}
return -1;
}
/**
* 在头部插入
*
* @param element
* @return
*/
public boolean addFirst(T element) {
linkFirst(element);
return true;
}
/**
* 在指定位置插入元素
*
* @param index
* @param element
* @return
*/
public boolean insert(int index, T element) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("插入位置超出链表范围");
if (index == 0)
linkFirst(element);
else {
//获取插入位置的前驱结点
Node preNode = getNodeByIndexBefore(index);
//获取插入位置的后继结点
Node nextNode = preNode.next;
Node newNode = new Node(element, preNode, nextNode);
//让前驱结点的后继指向插入结点
preNode.next = newNode;
//让后驱结点的前继指向插入结点
nextNode.prev = newNode;
size++;
}
return true;
}
/**
* 删除元素
*
* @param index
* @return
*/
public T delete(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("删除位置超出链表范围");
Node currentNode = header;
//删除的是头结点
if (index == 0) {
header = header.next;
currentNode.next = null;
header.prev = null;
} else {
Node currentNodeBefore = null;
for (int i = 0; i < index; i++) {
currentNodeBefore = currentNode;//前置结点
currentNode = currentNode.next; //要删除的当前结点
}
//删除的是尾结点
if (index == size - 1) {
tail = currentNodeBefore; //尾结点变为删除结点的前置结点
tail.next = null;
} else {
//将前置结点的后继指针指向当前结点的后继指针
currentNodeBefore.next = currentNode.next;
//将当前删除结点的后继结点的前驱指针指向删除结点的前驱结点
currentNode.next.prev = currentNodeBefore;
}
currentNode.prev = null;
currentNode.next = null;
}
size--;
return (T) currentNode.data;
}
//删除最后一个元素
public T remove() {
return delete(size - 1);
}
/**
* 尾部插入
*
* @param e
*/
private void linkLast(T e) {
final Node<T> l = tail;
final Node<T> newNode = new Node<>(e, null, null);
if (l == null) {
header = newNode;
tail = header;
} else {
tail.next = newNode; //尾结点指向新结点
newNode.prev = tail;
tail = newNode; //新结点作为尾结点
}
size++;
}
/**
* 在头部插入
*
* @param e
*/
private void linkFirst(T e) {
final Node<T> l = header;
Node<T> newNode = new Node<>(e, null, null);
if (l == null) {
header = newNode;
tail = header;
} else {
newNode.next = header;
header.prev = newNode;
header = newNode;
}
size++;
}
public boolean isEmpty() {
return size == 0;
}
//清空线性表
public void clear() {
//将头结点和尾结点设为空
header = null;
tail = null;
size = 0;
}
@Override
public String toString() {
if (isEmpty())
return "[]";
else {
StringBuilder sb = new StringBuilder("[");
for (Node current = header; current != null; current = current.next)
sb.append(current.data.toString() + "->");
sb.append("]");
int len = sb.length();
return sb.delete(len - 3, len - 1).toString();
}
}
}
测试代码
package likend;
/**
* Created by yxf on 2018/4/5.
*/
public class DoubleLinkTest {
public static void main(String[] args) {
DoubleLink ls = new DoubleLink();
ls.add(2);
ls.add(4);
ls.add(5);
ls.addFirst(1);
System.out.println("添加元素后的链表为: " + ls);
ls.insert(2, 3);
System.out.println("在链表位置2插入元素: " + ls);
ls.delete(2);
System.out.println("在链表位置2删除元素: " + ls);
ls.remove();
System.out.println("删除链表中的一个元素: " + ls);
System.out.println("获得链表位置为2处的元素: " + ls.getElement(2));
System.out.println("获取元素2的前驱元素: " + ls.priorElement(2));
System.out.println("获取元素2的后驱元素: " + ls.nextElement(2));
ls.clear();
System.out.println(ls);
}
}
添加元素后的链表为: [1->2->4->5]
在链表位置2插入元素: [1->2->3->4->5]
在链表位置2删除元素: [1->2->4->5]
删除链表中的一个元素: [1->2->4]
获得链表位置为2处的元素: 4
获取元素2的前驱元素: 1
获取元素2的后驱元素: 4
[]
Process finished with exit code 0
借鉴 https://blog.csdn.net/u013393958/article/details/72650496 代码有所改变。