用JavaScript刷leetcode第206题-反转链表(迭代法 + 递归法)

609 阅读2分钟

前言

本文将用两种方式来解题

  • 迭代法(依次改变指针指向)
  • 递归法(递推公式的作用是 将拿到的链表反转 并返回新的头节点)

一、题目描述

题目地址 | 代码地址
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

输入: head = [1,2,3,4,5]
输出: [5,4,3,2,1]

二、解题

2.1 迭代法

2.1.1 迭代思路:

  • 迭代,依次改变每个节点的指向
  • 改变节点指向时,如何做到后续节点不丢失?声明变量保存即可
  • 什么时候迭代结束?尾节点
  • 边界处理-什么样的链表不需要反转?空链表和只有一个节点的链表

2.1.2 迭代分解图-根据代码划分

未进入循环
反转后的头newHead + 待改节点cur

1.jpg

进入循环第一行代码
保存后续节点,以防丢失

2.jpg 进入循环第二行代码
改变当前节点指向,此时不用担心后续节点丢失,因为第一行代码已经保存

3.jpg

进入循环第三行代码
反转后的头节点已经改变,newHead应该指向新的头节点

4.jpg

进入循环第四行代码
上面其实已经完成一次节点的指向改变了,我们要处理下一个节点了

5.jpg

2.2.3 迭代法的代码

let reverseList = function (head) {
  // 边界处理 ---  如果链表为空,或者链表只有一个节点,则不需要反转,直接返回即可
  if(head === null || head.next === null) return head;

  // 存储反转后的头节点
  let newHead = null;
  // 当前 待改变指向 节点
  let cur = head;

  while(cur) {
    // 用来保存当前节点的下一个节点,目的保证后续节点不丢失
    const curNext = cur.next;
    // 改变当前节点的指向, 指向反转后的头节点
    cur.next = newHead;
    // 反转后的头节点已经变成cur了
    newHead = cur;
    // 处理后当前节点,现在要处理后面一个节点了
    cur = curNext;
  }

  // 整个循环结束,返回反转后的头节点
  return newHead;
}

2.2 递归法

2.2.1 递归思路

  • 首先明白递推公式的作用:把已拿到的链表反转,并返回反转后链表的头节点
  • 递归结束的条件:到了尾节点

2.2.2 递归图解

递推公式

let newHead = reverseList(head.next)

21.jpg

在反转后的链表后新增一个节点,这里新增的就是1节点

head.next.next = head;

22.jpg

处理循环引用

 head.next = null;

23.jpg

2.2.3 递归代码

let reverseList = function (head) {
  // 递归结束条件是到了尾节点
  if(head === null || head.next === null) return head;
  // 递推公式 - 将拿到的节点反转,并返回新的头节点;
  let newHead = reverseList(head.next);
  // 这行代码实际是改变反转后的链表尾节点的指向,指向当前的head
  head.next.next = head;
  // 当前 head 指向 null,
  head.next = null;

  return newHead;
}