206. 反转链表

213 阅读2分钟

反转一个单链表。

示例:

输入: 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]

  1. 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
  1. while 第二次执行
c = 4 -> 5
b.next = a;
b = 3 -> 2 -> 1
a = b, b = c;
a = 3 -> 2 -> 1, b = 4 -> 5
  1. 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]

执行过程

  1. 第一次执行到tail
tail = { val: 5, next: null }
head = 4 -> 5 -> null
  1. 执行head.next.next = head;
head.next = tail = { val: 5, next: null }
相当于tail.next = head = 4 -> 5 -> null
  1. 执行代码head.next = null
tail.next = 4 -> null
tail = 5 -> 4 -> null
  1. 重复1-3的过程

文章转载自:AcWing-LeetCode 206. Reverse Linked List