206 反转链表(JS)

213 阅读3分钟

反转链表

Category Difficulty Likes Dislikes
algorithms Easy (63.04%) 512 -
Tags

linked-list

Companies
反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶: 你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

/*
 * @lc app=leetcode.cn id=206 lang=javascript
 *
 * [206] 反转链表
 */
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function(head) {
    
};

1

一般链表的题我们会使用三个变量

  • prev
  • cur
  • next

具体的使用我们就在这道里讲,最后再总结

image-20190806162807143

反转后链表的的区别只在于每个节点的next发生了改变。

作为尾节点的next必然是指向NULL

既然是每个节点指向前继节点,作为链表的首位前继既然是无节点的,为了统一,我们会给链表前添加一个节点NULL

我们把前继节点称为prev,我们此时要操作的节点为cur,这里指上图的1,所以有

image-20190806171021026

let prev = null
let cur = head

按上面说的,倒序只不过是把当前节点的next改为前继节点,所以有

image-20190806171041782

cur.next = prev

此时,就会遇到一个问题,我们无法操控原本的老链表了,所以我们需要next,用来标记原本curnext,所以有

image-20190806171807171

let next = cur.next

只要我们不断重复上面的操作,左表的链表就是老链表的倒序了

为了回到"原本"的位置,我们要让prevcur老链表的“头节点”去,所以有

image-20190806171905972

cur = next 
prev = cur

小总结一下,我们在一个循环中,应该做哪些事

  1. 为了统一,假设 cur前有有一个前继节点,即prevprev = null

  2. 在操作cur改变其next指点,缓存下一个节点:next = cur.next

  3. 开始操作cur,使其指向前继节点cur.next = prev

  4. curprev回到"原本"的位置

    1. prev = cur
    2. cur = next

    注意这里的顺序是不能颠倒的

我们只要循环上述步骤1~3即可以倒序完整个链表,而停止的条件就是curNULL

image-20190806172012069

while (cur) {
  next = cur.next
  cur.next = prev
  prev = cur
  cur = next
}

最后我们只要返回prev就是可以了,所以全部代码为

var reverseList = function (head) {
  let [prev, cur, next] = [null, head, null]
  while (cur) {
    next = cur.next
    cur.next = prev
    prev = cur
    cur = next
  }
  return prev
};

2 利用ES6新特性

这道题难的不是写不出,难的是代码要写的优雅点

在上面我们使用了三个变量

  1. prev
  2. cur
  3. next

这个next的作用只是一个缓存,比如我们在进行两数交换的时候,如下

let a = 1
let b = 2
let temp = a
a = b
b = temp
console.log(a); // 2
console.log(b); // 1

上面代码中的temp就和我们使用next的本质是一样的:缓存

现在我们来看下面这段代码

let a = 1
let b = 2;
[a, b] = [b, a]
console.log(a); // 2
console.log(b); // 1

注意:因为第3行代码也是一个表达式,所以要在第2行代码后面添加;不然会报语法错误

我们在不使用temp变量缓存的情况下,就完成了两数交换,原因是这种语法帮我们做了缓存:

在赋值前,会把右边表达式变量存储的值先取出来,然后再进行赋值.

image-20190806173449276

那根据这种写法,我们再来思考原本的代码

var reverseList = function (head) {
  let [prev, cur, next] = [null, head, null]
  while (cur) {
    next = cur.next
    cur.next = prev
    prev = cur
    cur = next
  }
  return prev
}

那我们就可以不使用next

  1. 当前节点的next指向前继节点:cur.next = prev
  2. curprev往后移动一位,操作新的“头”节点
    1. prev = cur
    2. cur = cur.next
var reverseList = function (head) {
  let [prev, cur] = [null, head]
  while (cur) {
    [cur.next, prev, cur] = [prev, cur, cur.next]
  }
  return prev
};