双向链表的对象结构:
可以先提前一分钟思考一下这个链表的结构。
public static class DoubleNode implements Cloneable{
public int value;
public DoubleNode next;
public DoubleNode last;
public DoubleNode(int data) {
this.value = data;
}
@Override
public DoubleNode clone() {
try {
DoubleNode clone = (DoubleNode) super.clone();
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
算法推理
链表反转的值的效果:
1 2 3 4 5
5 4 3 2 1
某一个head节点指针转向后的效果:
第一层交换前:
lastNode(null) head nextNode node node node null
第一层交换后:
nextNode(null) head lastNode node node node null
循环体条件:while(head!=null)
注意:以下代码为算法的逐步推断。属于拆解法,逆向分解,正向思维,适合于不愿意看图的同学。
入函数之前head的状态:
head.value=1 head.last 为null head.next为2
注意:每一步思考其存在的问题,找到问题,写在纸上,带着问题继续看就可以,无需纠结任何一步,看两遍就会了。
step1: 我们先闭着眼睛将第一个head的next和last指针推向相反的方向,
head.last = head.next;
head.next = null;
step2:为了合理互换,先无脑增加空杯法,同样可以做到指针转换:
DoubleNode pre = null; DoubleNode next = null;
next = head.next;
head.last=next;
head.next=pre;
step3:next现在可以传给循环体了,
// 循环体结构为:
while(head!=null){
.....
}
//用next杯的目的:
//把next赋值给head,交给下一次循环,留给其去做下一次的指针变换。第一个空杯next完成闭环。
next = head.next;
head.last=next;
head.next=pre;
head=next;
step4:pre就是当前转换指针后的node,也就是下一次循环中head的前一个node节点
所以只需要关注以下这三行
head.next=pre;
pre=head;
head=next;
① pre临时存储了本次的转换后的head节点,
② pre改变后可以配合下一循环进行指针转向。(所以pre的使用不会在单次循环中关闭)
③ 因为后来head改为next了,成下一个了,所以要记录pre,要在head变化之前记录下pre。
step5:合并代码: 注意,返回值才是翻转后的链表。
public static DoubleNode reverseDoubleList(DoubleNode head) {
DoubleNode pre = null;
DoubleNode next = null;
while (head != null) {
next = head.next;
head.next = pre;
head.last = next;
pre = head;
head = next;
}
return pre;
}