通过单链表可以看到,有了next指针,使得我们在查找下一节点的时间复杂度变为O(1),但是如果想要查找上一个结点,则需要重新从头开始遍历,最坏的时间复杂度变为O(n)。双向链表提供了这种能力,即允许向前遍历,也允许向后遍历。实现这种特性的关键在于每个链结点既有下一个链结点的引用,也有上一个链结点的引用。
双向链表
双向链表是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。
双向链表的插入和删除并不是十分复杂,不过还是要注意顺序。
插入 顺序:先搞定s结点的前驱和后继,再搞定后结点的前驱,最后解决前结点的后继(遍历到插入位置的前一个结点)
删除
手写双向循环链表
类结构图
双向循环链表封装类
package com.company.datastructure;
public class MyDoubleLinkedList<E> {
private class Node<E>{
public E element; //元素值
private Node<E> prior; //直接前驱指针
private Node<E> next; //直接后继指针
public Node(){
}
public Node(E element) {
this.element = element;
}
}
private Node<E> head; //头结点
private int size; //链表长度 默认初始化为0
public MyDoubleLinkedList(){
head = new Node<E>();
head.prior = head; //初始全为空
head.next = head;
}
//插入
/**
* 指定位置插入结点
* @param index 索引值
* @param element 元素值
*/
public void insert(int index,E element){
rangeCheck(index);
Node<E> newNode = new Node<>(element);
Node<E> temp = head;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
newNode.prior = temp;
newNode.next = temp.next;
temp.next.prior = newNode;
temp.next = newNode;
size++;
}
/**
* 表头插入结点
* @param element 结点元素值
*/
public void addFirst(E element){
Node<E> newNode = new Node<>(element);
newNode.prior = head;
newNode.next = head.next;
head.next.prior = newNode;
head.next = newNode;
size++;
}
/**
* 表尾插入结点
* @param element 结点元素值
*/
public void addLast(E element){
Node<E> newNode = new Node<>(element);
Node<E> temp = head.prior;
newNode.prior = temp;
newNode.next = temp.next;
temp.next.prior = newNode;
temp.next = newNode;
size++;
}
//删除
/**
* 删除指定位置元素
* @param index 索引值
* @return 删除元素值
*/
public E remove(int index){
rangeCheck(index);
Node<E> temp = head.next;
//调用get方法获得索引位置结点时需要把0独立出来,所以就直接遍历到所需结点
for (int i = 0; i < index; i++) {
temp = temp.next;
}
temp.prior.next = temp.next;
temp.next.prior = temp.prior;
size--;
return temp.element;
}
/**
* 删除表头元素
* @return 被删除元素值
*/
public E removeFirst(){
Node<E> temp = head.next;
temp.prior.next = temp.next;
temp.next.prior = temp.prior;
size--;
return temp.element;
}
/**
* 删除表尾元素
* @return 被删除元素值
*/
public E removeLast(){
Node<E> temp = head.prior;
temp.prior.next = temp.next;
temp.next.prior = temp.prior;
size--;
return temp.element;
}
//查找
/**
* 根据索引值查找元素
* 索引小于链表长度一半时从前往后遍历,否则从后往前遍历
* @param index 索引
* @return 索引处元素值
*/
public E get(int index){
rangeCheck(index);
if(index<=size/2){
Node<E> temp = head.next;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
return temp.element;
}else{
Node<E> temp = head.prior;
for (int i = size-1; i >index ; i--) {
temp = temp.prior;
}
return temp.element;
}
}
/*---------------------------------------------------------------------*/
/**
* 获得链表长度
* @return 链表长度
*/
public int getSize(){
return size;
}
/**
* 判断链表是否为空
* @return true为空,false非空
*/
public boolean isEmpty(){
return size==0?true:false;
}
/**
* 清空链表
*/
public void clear(){
Node<E> temp = head.next;
for (int i = 0; i < size; i++) {
Node<E> next = temp.next;
temp.element = null;
temp.prior = null;
temp.next = null;
temp = next;
}
head.next = head;
head.prior = head;
size = 0;
}
/*---------------------------------------------------------------------*/
/**
* 索引检查
* @param index 索引
*/
private void rangeCheck(int index){
if (index <0|| index >= size){
throw new IndexOutOfBoundsException("索引不合法"+index);
}
}
}
双向循环链表测试
package com.company.datastructure;
public class TestMyDoubleLinkedList {
public static void main(String[] args) {
MyDoubleLinkedList<String> mdl = new MyDoubleLinkedList<>();
mdl.addLast("chen2");
mdl.addLast("chen3");
mdl.addLast("chen4");
mdl.addFirst("chen0");
mdl.insert(1,"chen1");
System.out.println(mdl.getSize());
for (int i = 0; i < mdl.getSize(); i++) {
System.out.println(mdl.get(i));
}
mdl.remove(1);
mdl.removeFirst();
mdl.removeLast();
System.out.println(mdl.getSize());
for (int i = 0; i < mdl.getSize(); i++) {
System.out.println(mdl.get(i));
}
mdl.clear();
System.out.println(mdl.getSize());
System.out.println(mdl.isEmpty());
}
}