前言
讲双向链表之前,首先我们要先了解双向链表的结构是怎么样的?下面我用一张图片来介绍.
可以看到下面的图片,双向链表有三个区,分别代表的是 val:数值区 prev:前一个节点的地址 next:下一个节点的地址.
了解完单链表之后,双链表写的跟玩的一样.看过了双链表方法的实现就知道了话不多说,我们进入正题.
头插法
先来一个开胃小菜,头插法.头插法就是在链表的开头插入节点,要考虑两种情况.
第一种情况:当链表为空时,新插入的节点既是头节点也是尾节点,所以head和tail都指向新插入的节点.
第二种情况:当链表不为空时,要改变头节点的prev和新插入节点的next的指向.
处理完了上面的两种情况,记得要将head的指向改一下.
public void addFirst(int data){
Note note = new Note(data);
if(this.head == null){
this.head = note;
this.tail = note;
return;
}
this.head.prev = note;
note.next = head;
this.head = note;
}
尾插法
尾插也是同理,注意最后修改指向就行了.
public void addLast(int data){
Note note = new Note(data);
if(this.head == null){
this.head = note;
this.tail = note;
return;
}
this.tail.next = note;
note.prev = tail;
this.tail = note;
}
在任意位置插入
看整个链表,插入的位置只有两个地方.
第一个:头部和尾部
第二个:中间
有一个很容易忽视的地方,涉及插入的位置的时候,要先判断插入的位置是否合法,不能小于零并且不能隔着位置插入.
在index位置插入元素,首要任务是要找到index位置,就有了找cur的方法,到了这里插入的准备工作就做完了,假设是在1的位置插入,具体实现插入看下面的动态图片.
public boolean addIndex(int index,int data)throws IndexWrongfulException{
if(index < 0 || index > size()){
throw new IndexWrongfulException("位置非法,请重新输入");
}
if(index == 0){
addFirst(data);
return true;
}
if(index == size()){
addLast(data);
return true;
}
Note note = new Note(data);
Note cur = findCur(data);
note.next = cur;
note.prev = cur.prev;
cur.prev.next = note;
cur.prev = note;
return true;
}
private Note findCur(int index){
Note cur = this.head;
while(index != 0){
cur = cur.next;
}
return cur;
}
查看是否包含关键字key
这个方法实现思路就是利用while循环让cur走,找到了就返回true,没找到就返回false.
public boolean contains(int key){
Note cur = this.head;
while(cur != null){
if(cur.val == key){
return true;
}
cur = cur.next;
}
return false;
}
删除第一次出现的关键字key
注意我这里说的是第一次出现的关键字key,后面假如再出现key不用删除,例如: 1 2 3 4 4,假如要删除的关键字key是4,删除之后的结果就是1 2 3 4.就只删除了前一个4,而后面的4不用被删除.
介绍完了规则之后就是代码的具体细节了,整个删除逻辑核心代码是这两行,
cur.prev.next = cur.next;
cur.next.prev = cur.prev;
被数据结构折磨了这么久,看完这两行代码,大脑条件反射会注意到这样一个问题,这两行代码会不会出现空指针异常的情况?答案是肯定的.
当要删除的节点是头节点时,上面第一行代码会报空指针异常.因为head的prev为null,所以会报空指针异常.
当要删除的节点是尾节点时,上面第二行代码会报空指针异常,因为tail的next为null,所以会报空指针异常.
那么我们要怎样处理这两尊神仙呢?
我们先来看第一行代码可能出现空指针异常的情况.结合图片来看,可以把cur.prev = null情况单独处理,cur.prev = null肯定就是头节点,我们只要改变head节点的指向就行了,然后才能走cur.prev.next == cur.next; 这行代码.
当要删除3号节点的时候,cur.prev.next = cur.next;这行代码是可以正常走的,cur.next == null的情况就要单独考虑了,处理完空指针的情况才能走cur.next.prev = cur.prev;这行代码.
删完之后就结束这个方法.
public void remove(int key){
Note cur = this.head;
while(cur != null){
if(cur.val == key){
if(cur.prev == null){
this.head = this.head.next;
head.prev = null;
}else{
cur.prev.next = cur.next;
if(cur.next == null){
this.tail = tail.prev;
this.tail.next = null;
}else {
cur.next.prev = cur.prev;
}
}
return;
}
cur = cur.next;
}
}
删除所有关键字为key的节点
这个方法就是删除所有要删除的节点,就不是上面删除第一次出现的关键字key了.
会了上面的方法,这个方法就很简单了,只要将return去掉就行了.有的人就不懂了,仔细想想,上面的方法是删除完了要删除的节点就结束方法,假如删除完不结束方法,就会继续删除后面要删除的节点.这样就会达到这个方法的目的.
总结
好啦!以上就是双链表的所有的内容,如果对你有帮助的话,麻烦点一个免费的赞和关注.