反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶: 你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
算法1
(链表操作,迭代) O(n) 翻转即将所有节点的next指针指向前驱节点。 由于是单链表,我们在迭代时不能直接找到前驱节点,所以我们需要一个额外的指针保存前驱节点。同时在改变当前节点的next指针前,不要忘记保存它的后继节点。
空间复杂度分析:遍历时只有3个额外变量,所以额外的空间复杂度是 O(1)。 时间复杂度分析:只遍历一次链表,时间复杂度是 O(n)。
JS代码
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var reverseList = function(head) {
if(!head) return null;
let a = head, b = a.next;
while(b) {
let c = b.next;
b.next = a;
a = b, b = c;
}
head.next = null;
return a;
};
例如:链表 [1,2,3,4,5]
- while 第一次执行
b = 2 -> 3 -> 4 -> 5, c = 3 -> 4 -> 5
b.next = a;
b = 2 -> 1
a = b, b = c;
a = 2 -> 1, b = 3 -> 4 -> 5
- while 第二次执行
c = 4 -> 5
b.next = a;
b = 3 -> 2 -> 1
a = b, b = c;
a = 3 -> 2 -> 1, b = 4 -> 5
- while 第三次执行
c = 5
b.next = a;
b = 4 -> 3 -> 2 -> 1
a = b, b = c;
a = 4 -> 3 -> 2 -> 1, b = 5
解法2: 递归
(链表操作,递归) O(n) 首先我们先考虑 reverseList 函数能做什么,它可以翻转一个链表,并返回新链表的头节点,也就是原链表的尾节点。 所以我们可以先递归处理 reverseList(head->next),这样我们可以将以head->next为头节点的链表翻转,并得到原链表的尾节点tail,此时head->next是新链表的尾节点,我们令它的next指针指向head,并将head->next指向空即可将整个链表翻转,且新链表的头节点是tail。
空间复杂度分析:总共递归 n 层,系统栈的空间复杂度是 O(n),所以总共需要额外 O(n) 的空间。 时间复杂度分析:链表中每个节点只被遍历一次,所以时间复杂度是 O(n)。
JS代码
var reverseList = function (head) {
if (!head || !head.next) return head;
let tail = reverseList(head.next);
head.next.next = head;
head.next = null;
return tail;
};
例如:链表 [1,2,3,4,5]
执行过程
- 第一次执行到tail
tail = { val: 5, next: null }
head = 4 -> 5 -> null
- 执行
head.next.next = head;
head.next = tail = { val: 5, next: null }
相当于tail.next = head = 4 -> 5 -> null
- 执行代码
head.next = null
tail.next = 4 -> null
tail = 5 -> 4 -> null
- 重复1-3的过程