概述
首先我们来认识一下双向链表,双向链表就是链表中的各个节点即都知道自己的前一个节点的值又知到自己后一个节点的值,如图所示就像这样。
也就是说,双向链表的每个节点有两个指针,一个指针指向前一个节点,一个指向后一个节点,那么我们就能轻松构建出节点类了,一共有三个属性值,头指针,尾指针,以及值,代码演示一下。
static class Node{
Node prev;//指向上一个节点的指针
int value;//节点的值
Node next;//指向下一个结点的指针
public Node(Node prev, Node next, int value) {
this.prev = prev;
this.value = value;
this.next = next;
}
}
private Node head;//头哨兵
private Node tail;//尾哨兵
public DoublyLinkedList(){
head=new Node(null,null,666);
tail=new Node(null,null,888);
head.next=tail;
tail.prev=head;
}
相关操作
1.根据索引查找链表中节点
直接循环遍历链表再加一个if语句判断一下就可以了。
//循环遍历
private Node findNode(int index){
int i=-1;
for(Node p=head;p!=tail;p=p.next,i++){
if(i == index){
return p;
}
}
return null;
}
2.向链表索引位置添加节点
不多说先上图,后解释。
从图中我们可以看到插入操作是让插入位置的上一个节点的next指针指向插入的节点,插入的节点的prev指针指向插入位置的前一个结点,插入位置的指针的next指向插入位置前结点的next节点,插入位置前一个结点的next节点的prev指针指向插入位置的节点,我们的代码只需要完成上述四步操作就可以了,上代码。
//向索引位置添加节点
public void addNode(int index,int value){
Node prev=findNode(index-1);
if(prev==null){
System.out.println("你输入的index不合法");
}
Node next=prev.next;
Node node=new Node(prev,next,value);
prev.next=node;
next.prev=node;
}
特殊情况尾部插入法,因为我们有尾部哨兵,所以不用遍历链表就能找到尾部节点,就是尾哨兵的prev结点指向的,插入方式不变。
public void addLast(int value){
//尾部节点,因为尾部哨兵的存在所以我们可以直接获得尾部节点
Node lastNode = tail.prev;
Node addLastNode=new Node(lastNode,tail,value);
lastNode.next=addLastNode;
tail.prev=addLastNode;
}
3.按照索引删除链表节点
上图解释
从图中我们可以看到, 删除一个节点就是让索引位置的前一个结点的next指针指向索引位置的后一个节点,让索引位置的后一个节点的prev指针指向索引位置的前一个结点,我们的代码只需要完成上述操作就ok了,上代码。
//按照索引删除链表节点
public void remove(int index){
Node prev=findNode(index);
Node prevNode=prev.prev;//索引位置的前一个结点
if(prevNode==null && prev==tail){
//哨兵节点不能删除
System.out.println("你输入的index不合法");
}
Node nextNode=prev.next;//索引位置的后一个节点
prevNode.next=nextNode;
nextNode.prev=prevNode;
}
尾部节点删除的话是一种特殊情况,我们知到尾部节点,不用找了,然后进行上面操作。
//尾部节点删除
public void removeLast(){
Node lastNode=tail.prev;
if(lastNode==head){
System.out.println("链表已经空了,不能删除!");
}
lastNode.next=tail;
tail.prev=lastNode.prev;
}
4. 遍历链表
遍历链表的话就不多做说明了直接上代码。
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>()//迭代器对象
{
Node p=head.next;//遍历起始位置
@Override
public boolean hasNext() {
return p!=tail;
}
@Override
public Integer next() {
int value = p.value;
p=p.next;
return value;
}
};
}
总结
双向链表的原理其实和单向链表差不多,各种操作也是,简单理解就好, 希望我的文章可以更好的帮助到你,如有疑问评论区指出,感谢你的浏览。