双向链表
之前讨论的链式存储结构中的节点中只有一个指示直接后继的指针域,由此,从某个节点出发只能顺指针往后寻找其他节点.若要寻找的节点的直接前驱,则需从表头出发.换句话说,在单链表中,NextElem的执行时间为O(1),而PriorElem的执行时间为O(n).为克服单链表这种单向性的缺点,可利用双向链表.
双向链表的节点中有两个指针域,其一指向直接后继,另一指向直接前驱
和单链表相似,双向链表也可以有循环表,如下图所示,链表中有两个环.
package main.com.cs.test;
public class DoublyLinkedList<T> {
private int size;
private Node<T> head;//链表的投节点
private Node<T> tail;//链表的尾节点
//构建一个null 链表
public DoublyLinkedList() {
this.head =this.tail= new Node<>();
}
private static class Node<T>{
Node<T> Precursor;
T data;
Node<T> next;
public Node(T data){
this(data, null, null);
}
public Node(){
this(null, null, null);
}
public String toString(){
return this.data.toString();
}
Node(T data, Node<T> front, Node<T> next){
this.Precursor = front;
this.data = data;
this.next = next;
}
}
public boolean add(int index, T data) {
if(index<0||data==null)
throw new NullPointerException("index < 0 || data == null");
int j = 0;
Node<T> front = this.head;
//查找要插入结点位置的前一个结点
while (front.next != null && j < index) {
j++;
front = front.next;
}
//创建需要插入的结点,并让其前继指针指向front,后继指针指向front.next
Node<T> q = new Node<T>(data, front, front.next);
//空双链表插入和尾部插入,无需此操作
if(front.next != null) {
//更改front.next的前继指针
front.next.Precursor = q;
}
//更改front的后继指针
front.next = q;
//在尾部插入时需要注意更新tail指向
if(front==this.tail){
this.tail=q;
}
return true;
}
public void printLinkList() { //打印链表
DoublyLinkedList.Node<T> curr = this.head;
if (isEmpty()) {
try {
throw new Exception("linklist is empty");
} catch (Exception e) {
e.printStackTrace();
}
}
while(curr != null){
System.out.print(curr.data+" ");
//判断如果 尾节点的 next 指向 头节点 那么就跳出循环
if (curr.next == head){
return;
}
curr = curr.next;
}
}
public boolean isEmpty(){//判断链表是否为空
return this.head == null;
}
public static void main(String[] args) {
DoublyLinkedList<Integer> mylist = new DoublyLinkedList<Integer>();//构造一个空链表
mylist.add(0,5);
mylist.add(1,3);
mylist.add(2,7);
mylist.add(3,6);
mylist.printLinkList();
}
}