关于LeetCode反转链表迭代解法的两处核心疑问
我在LeetCode上看到反转链表问题,尝试了如下JS迭代解法,但存在两处疑惑:
var reverseList = function(head){ var tmp = null; var newHead = null; while(head !== null){ tmp = head; head = head.next; tmp.next = newHead; newHead = tmp; } return newHead; }
输入为[5,4,3,2,1]时,迭代过程如下:
[1,2,3,4,5] [2,3,4,5] null [1] [2,3,4,5] [3,4,5] [1] [2,1] [3,4,5] [4,5] [2,1] [3,2,1] [4,5] [5] [3,2,1] [4,3,2,1] [5] null [4,3,2,1] [5,4,3,2,1]
疑惑解答
1. 为什么执行tmp = head; //tmp = [1,2,3,4,5]后,newHead = tmp得到的是[1]而非[1,2,3,4,5]?
这是因为链表是引用类型,tmp = head只是让tmp指向原链表的头节点(值为1的节点),但之后执行了tmp.next = newHead——第一次循环时newHead是null,这直接切断了该节点与原链表后续节点的链接。此时这个节点的next被设为null,不再指向原链表中值为2的节点,所以newHead = tmp后,newHead指向的是一个仅包含值1、next为null的节点,也就是你看到的[1]。
你注释里的tmp = [1,2,3,4,5]是简化表述,实际上tmp只是指向链表头节点,后续的修改会直接改变该节点的结构。
2. head = head.next;当前位置不影响后续代码,但移到循环最后一行时代码失效,原因是什么?
如果把head = head.next;移到循环最后,执行顺序变为:
while(head !== null){ tmp = head; tmp.next = newHead; newHead = tmp; head = head.next; // 移至循环末尾 }
问题出在tmp = head后,tmp.next = newHead会修改head指向的节点的next指针(因为tmp和head指向同一个节点)。此时head.next已经不是原链表的下一个节点,而是被改成了newHead的值(第一次循环为null)。当最后执行head = head.next时,head直接变为null,循环仅执行一次就终止,无法遍历剩余节点,自然无法完成整个链表的反转。
原代码中先执行head = head.next,是在修改tmp的next指针前就将head移动到下一个节点,这样即使后续修改tmp的next,也不会影响已经保存好的head指向,确保循环能遍历所有节点。
内容的提问来源于stack exchange,提问作者LostProto

