JAVA
① 逐个反转
public class Solution {
public ListNode reverseList(ListNode head) {
ListNode ans = null;
while (head != null) {
ListNode tmp = head.next;
head.next = ans;
ans = head;
head = tmp;
}
return ans;
}
}
动态图来自LeetCode题解
其实我感觉并不是很好理解 (却是最常用的) ,这种方法属于重新创建链表 ans 但也用到了原链表 head,每次将 head 链表反转一个结点,同时又用 ans 替代了原 head,一直持续到 null
② 神奇的递归
public class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null)
return head;
ListNode tmp = reverseList(head.next);
head.next.next = head;
head.next = null;
return tmp;
}
}
动态图来自LeetCode题解
思路是先一路递归到最后,每次调用回来的时候,先让 tmp 赋值为上次调用回来的 head,再让当前的 head 的 next.next 赋值为 head(也就是数组下标为 head 的节点,它所指向的节点指向回自己),而 head(自己)赋值为 null(指向空),最后返回 tmp
没错,挺有趣的解法,但当我写完后一直有个很困惑的点,就是为什么 tmp 赋值为 head.next 后,当 head.next.next 改变了,tmp.next 也跟着改变了?(因为最后传递的是 tmp,tmp 原本是指向数组的 null,但最后输出的是反转后的数组,所以推出 tmp.next 也变了)
为了验证,我写了下面的代码
//head x xx
ListNode head = new ListNode(4);
ListNode x = new ListNode(5);
ListNode xx = new ListNode(100);
head.next = x;
x.next = xx;
ListNode tmp = head.next;
System.out.println(tmp.next.val); // 100
head.next.next = head;
System.out.println(tmp.next.val); // 100
head.next = null;
System.out.println(tmp.next.val); // 100
我仿照样例给出数组为 [1,2,3,4,5]
的简化链表,上面例子中 head 相当于 4,x 相当于 5,xx 相当于 null,和原代码一样,但在每个步骤后都输出一次 tmp.next 的值,看是否会发生变化(注释后面是我以为会得到的值)
打印如下:
100
4
4
很明显我想错了,于是我:
head.next.next = head;
改成
xx = head
打印如下:
100
100
100
那么结果很显然了,head.next.next 确实可以影响 tmp.next
我给出的解释是:head.next.next 的第二个 next 相当于 C语言 的指针,其改变的不是值,而是指向的地址
(这就是没有指针的 JAVA 吗?爱了爱了)
Python
① 逐个反转
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
ans = None
while head:
head.next, ans, head = ans, head, head.next
return ans
这用的其实就是第一种方法的思想,仔细看看 JAVA 的代码的话,就会发现实质就是三个变量在相互赋值(head.next 、ans 、head)
注意:用平行赋值语法,系统会自动生成一个 tmp 储存 head.next,并从前往后依次赋值
② 神奇的递归
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
def reans(ans, pre):
if not ans: return pre # 终止条件
res = reans(ans.next, ans) # 递归后继节点
ans.next = pre # 修改节点引用指向
return res # 返回反转链表的头节点
return reans(head, None) # 调用递归并返回
LeetCode图解(看了这个就懂了)
但和 JAVA 一样,总觉得用递归非常不直观,传参和调用中暗藏着看不见的指针在作怪