反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
输入处理: head为空直接返回空.
1、迭代法
思路: 设置一个前指针pre和当前指针cur,推进直到cur为空,返回pre。
复杂度分析:
时间: O(n). 逐个推进故 O(n).
空间: O(1). 只用到了常数级额外空间故 O(1).
var reverseList = function(head) {
let cur = head,pre = null;
while(cur){
let temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
//[cur.next,pre,cur] = [pre,cur,cur.next];
}
return pre;
};
2、自递归法 – 反转后尾接
思路: 自递归无法存储推进状态所以无法尾递归,不断将 next 放入递归方法反转链表,结果.next = 当前节点. (Tip: 记得推进结果直到 next.next 为空)
复杂度分析:
时间: O(n). 从最底层两个节点反转开始,每层时间复杂度均为 O(1), 总共 n-1 层递归,故时间复杂度为 O(n).
空间: O(n). 递归调用栈消耗空间,共 n-1 层递归,故空间复杂度为 O(n).
var reverseList = function(head) {
if (!head || !head.next) return head;
let next = head.next; // next节点,反转后是最后一个节点
let reverseHead = reverseList(next);
head.next = null; // 裁减 head
next.next = head; // 尾接
return reverseHead;
};
3、尾递归法
思路: 用 prev 和 curr 存储推进状态,直到 curr 为空则输出结果.
复杂度分析:
时间: O(n). 等同于正常推进,故 O(n).
空间: O(1). 尾递归方式,重复使用一个空间故空间复杂度为 O(1).
var reverseList = function(head) {
return reverse(null, head);
};
function reverse (prev, curr) {
if (!curr) return prev;
// [curr.next, prev, curr] = [prev, curr.next, curr.next];
let tmp = curr.next;
curr.next = prev;
return reverse(curr, tmp);
}
4、转数组后 reduce 构造出链表
思路:模仿自 转换成对应的数字直接相加
复杂度分析:
时间: O(n): 两轮遍历.
空间: O(1): 结果不算额外空间,但确实是非原地反转算法.
var reverseList = function(head) {
if (!head || !head.next) return head
const arr = []
while (head) {
arr.push(head.val)
head = head.next
}
return arr.reduce((acc, v) => { return { val: v, next: acc } }, null)
};
转载自力扣(LeetCode)
作者:Nodreame
链接:https://leetcode-cn.com/problems/reverse-linked-list/solution/js-san-chong-jie-fa-di-gui-wei-di-gui-die-dai-by-n/