图8-1
双哨兵是定义两个哨兵,一个头和一个尾,双向链表是每个元素都有一个前仆节点和后继节点,如图8-1所示。
图8-2
在首部插入元素一,让新节点前仆指向头节点,后继指向下一节点,头哨兵的后继指向新节点,后一节点的前仆指向新节点,如图8-2所示。
图8-3
在尾部插入元素,需要找到尾哨兵指向的前一个节点,让新节点的前仆指向前一个节点,后继指向尾哨兵,上一个节点的后继指向新节点,尾哨兵的前仆指向新节点,如图8-3所示。
图8-4
根据索引插入,需要找到该索引的上一个节点后下一个节点,新节点的前仆指向上一个节点,后继指向下一个节点,上一个节点的后继指向新节点,下一个节点的前仆指向新节点,如图8-4所示。
图8-5
删除头部元素,找到头哨兵,让头哨兵的后继指向待删除的下一个节点,下一个节点的前仆指向头哨兵,如图8-5所示。
图8-6
删除尾部元素,通过尾哨兵找到待删除的节点,通过待删除的节点找到上一个节点,使上一个节点的后继指向尾哨兵,尾哨兵的前仆指向上一个节点,如图8-6所示。
图8-7
根据索引删除,需要找到待删除的上一个节点和下一个节点,让上一个节点的后继指向下一个节点,下一个节点的前仆指向上一个节点,如图8-7所示。
代码实现如下:
//带哨兵的双向链表
public class DoubleLinkedListSentinel{
//测试在最后插入
@Test
public void testLast(){
addLast(1);
addLast(2);
addLast(3);
addLast(4);
loopLinked(); // 4 3 2 1
}
//测试在首部插入
@Test
public void testFirst(){
addLast(1);
addLast(2);
addLast(3);
addLast(4);
loopLinked(); // 1 2 3 4
}
//测试根据索引插入
@Test
public void testInsert(){
insert(0,0);
addLast(1);
insert(2,2);
addLast(3);
addLast(4);
insert(5,5);
loopLinked(); // 1 2 3 4
}
//测试删除
@Test
public void testRemoveFirst(){
addLast(1);
addLast(2);
addLast(3);
addLast(4);
removeFirst();
loopLinked(); //2 3 4
}
//测试删除
@Test
public void testRemoveLast(){
addLast(1);
addLast(2);
addLast(3);
addLast(4);
removeLast();
loopLinked(); //1 2 3
}
//测试根据索引删除
@Test
public void testRemove(){
addLast(1);
addLast(2);
addLast(3);
addLast(4);
remove(3);
loopLinked();
}
static class Node {
Node prev; //上一个指针
int value; //值
Node next; //下一个指针
public Node(Node prev, int value, Node next) {
this.prev = prev;
this.value = value;
this.next = next;
}
}
private Node head; //头哨兵
private Node tail; //尾哨兵
//根据索引查找节点
private Node findNode(int index){
int i=-1;
for (Node pointer=head;pointer!=tail;pointer=pointer.next,i++){
if (i==index){
return pointer;
}
}
return null;
}
public DoubleLinkedListSentinel() {
//初始化头尾哨兵
head = new Node(null, 8848, null);
tail = new Node(null, 8888, null);
//初始头哨兵下一个指向尾哨兵
head.next = tail;
//初始尾哨兵上一个指向头哨兵
tail.prev = head;
}
//头部插入
public void addFirst(int value) {
insert(0,value);
}
//头部删除
public void removeFirst() {
remove(0);
}
//尾部插入
public void addLast(int value) {
//找到尾节点的前仆
Node last = tail.prev;
//插入新节点
Node add = new Node(last, value, tail);
//新节点的前仆的后继指向新节点
last.next=add;
//尾节点的前仆指向新节点
tail.prev=add;
}
//尾部删除
public void removeLast() {
//找到删除的节点
Node remove = tail.prev;
if (remove==head){
throw new IllegalArgumentException("参数不合法:");
}
//找到删除的前一个节点
Node prev = remove.prev;
//待删除的前一个节点的后继指向尾指针
prev.next=tail;
//尾指针的前仆指向待删除的前一个节点
tail.prev=prev;
}
//根据索引插入
public void insert(int index,int value){
//找到插入的上一个节点
Node preNode = findNode(index - 1);
if (preNode==null){
throw new IllegalArgumentException("参数不合法:"+index);
}
//找到插入的下一个节点
Node nextNode = preNode.next;
//定义新节点
Node newNode = new Node(preNode, value, nextNode);
//上一个节点的下一个指向新节点
preNode.next=newNode;
//下一个节点的前一个指向新节点
nextNode.prev=newNode;
}
//根据索引删除
public void remove(int index){
//找到待删除的少一个节点
Node preNode = findNode(index - 1);
if (preNode==null){
throw new IllegalArgumentException("参数不合法:"+index);
}
//上一个节点的后继即是待删除元素
Node remove = preNode.next;
if (remove==tail){
throw new IllegalArgumentException("参数不合法:"+index);
}
//找到待删除的下一个
Node next = remove.next;
//删除一个元素只需上一个后继指向待删除的下一个,待删除的后继的前仆指向待删除的前仆
preNode.next=next;
next.prev=preNode;
}
//遍历链表
public void loopLinked(){
Node pointer=head.next;
while (pointer!=tail){
System.out.println(pointer.value);
pointer=pointer.next;
}
}
}