24. 反转链表
今天打了一天的王者荣耀 没好好学习......真不应该
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
限制:
0 <= 节点个数 <= 5000
利用外部空间
思路: 这种方式很简单,先申请一个动态扩容的数组或者容器,比如 ArrayList 这样的。然后不断遍历链表,将链表中的元素添加到这个容器中。再利用容器自身的 API,反转整个容器,这样就达到反转的效果了。最后同时遍历容器和链表,将链表中的值改为容器中的值。因为此时容器的值是:
5 4 3 2 1
链表按这个顺序重新被设置一边,就达到要求啦。
当然你可以可以再新创建 N 个节点,然后再返回,这样也可以达到目的。
这种方式很简单,但你在面试中这么做的话,面试官 100% 会追问是否有更优的方式,比如不用外部空间。所以我就不做代码和动画演示了,下面来看看如何用 用 O(1)O(1) 空间复杂度来实现这道题。
结果: 空间复杂度有点高
// ...........
双指针迭代
思路: 我们可以申请两个指针,第一个指针叫 pre,最初是指向 null 的。第二个指针 cur 指向 head,然后不断遍历 cur。每次迭代到 cur,都将 cur 的 next 指向 pre,然后 pre 和 cur 前进一位。都迭代完了(cur 变成 null 了),pre 就是最后一个节点了。
结果:
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function(head) {
let pre = null, cur = head, temp = null
while(cur != null) {
temp = cur.next
cur.next = pre
pre = cur
cur = temp
}
return pre
};
// 吓死我了 提交第一次 108ms 第二次80ms 第三次64ms 第四次就打败了95.86%的用户了
接下来尝试下递归的写法
递归解法
这题有个很骚气的递归解法,递归解法很不好理解,这里最好配合代码和动画一起理解。
递归的两个条件:
- 终止条件是当前节点或者下一个节点==null
- 在函数内部,改变节点的指向,也就是 head 的下一个节点指向 head 递归函数那句
很不好理解,其实就是 head 的下一个节点指向head。递归函数中每次返回的 cur 其实只最后一个节点,在递归函数内部,改变的是当前节点的指向。
var reverseList = function(head) {
if (head === null || head.next === null) {
return head
}
let newReversedList = reverseList(head.next)
head.next.next = head
head.next = null
return newReversedList
};