力扣19删除链表的倒数第 N 个结点:思路分析+图文全解+方法总结(快慢指针法&递归法)+深入思考

简介: 力扣19删除链表的倒数第 N 个结点:思路分析+图文全解+方法总结(快慢指针法&递归法)+深入思考

第一部分:题目描述

🏠 链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

⭐ 难度:中等

第二部分:代码实现

2.1 快慢指针

快慢指针,p1 指向待删节点的上一个,p2 先走 n + 1 步。

步骤:

  1. 快慢指针都指向哨兵 sentinel (创建sentinel节点,将 sentinel 的下一个节点设置为头节点 head)。
  2. fast 向后移动 n+1 个位置,使得 slow 与 fast 保持了 n+1 个距离。
  3. fast 和 slow一起向后移动(移动相同距离),直到 fast 到最后一个节点的下一个节点( null )。fast 和 slow 始终保持着 n+1 个位置的距离。要删除的倒数的第 n 个节点,就是 slow 的下一个节点。
  4. 删除节点就是将 改变 slow 的下一个节点为 slow 的下下个节点。
  5. 返回真正的头节点,就是 sentinel 的下一个节点。

图解分析:

已知链表 1 -> 2 -> 3 -> 4 -> 5,需要删除倒数第 2 个节点(n = 2)

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        // 链表:sentinel -> 1 -> 2 -> 3 -> 4 -> 5
        // n = 2,应当删除节点4
        // 哨兵节点,作为伪头节点
        ListNode sentinel = new ListNode(-1, head);
        // 快指针
        ListNode fast = sentinel;
        // 慢指针
        ListNode slow = sentinel;
        // 先将 快指针 移动到 慢指针 n+1 个位置后
        // 移动后 fast 指向 3,slow 指向 sentinel
        while (n > -1) {
            fast = fast.next;
            n--;
        }
        // 将快慢指针向后移动,知道 快指针到了最后一个节点的下一个节点 null
        // 此时 慢指针 指向节点的 下一个节点就是待删除的节点 (val = 3)
        while (fast != null) {
            slow = slow.next;
            fast = fast.next;
        }
        // 将当前慢指针指向节点的下一个节点更改为 待删除节点的下一个节点
        // 那么此时 3 -> 5
        slow.next = slow.next.next;
        // 返回真正的头节点
        return sentinel.next;
    }
}

❓ 为什么要设置一个sentinel

Answer:主要是考虑待删除的节点正好是头节点 head 的情况。我们知道在单链表中我们想要删除一个节点需要依靠它的上一个节点,而头节点head没有上一个节点,因此我们暂时给 head 前面加一个伪头节点 sentinel 指向 head。这样就能像其它节点一样通过待删除节点的上一个节点来删除待删除的节点。

2.2 递归

思路:写一个递归函数,用来返回下一个节点的倒数序号。

recursion(ListNode p=1, int n=2) {
    recursion(ListNode p=2, int n=2) {
      recursion(ListNode p=3, int n=2) {
        recursion(ListNode p=4, int n=2) {
          recursion(ListNode p=5, int n=2) {
            recursion(ListNode p=null, int n=2) {
              return 0; // 最内层序号0
          }
                    return 1; // 上一次返回值+1
        }
                return 2;
      }
            if(返回值 == n == 2) {
                // 删除 next
            }
            return 3;
    }
        return 4;
  }
    return 5;
}

但上述代码有一个问题,就是若删除的是第一个节点,它没有上一个节点,因此可以加一个哨兵来解决。

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode sentinel = new ListNode(-1, head);
        recursion(sentinel, n);
        return sentinel.next;
    }
    /**
     * @param p 当前节点
     * @param n 需要删除倒数第 n 个节点
     * @return 当前节点为 倒数第几个节点
     */
    private int recursion(ListNode p, int n) {
        // 如果无节点了,就返回 0
        if (p == null) {
            return 0;
        }
        // nth 代表的是下一个节点的倒数位置
        int nth = recursion(p.next, n);
        // 如果下一个节点是 第 n 个节点,就需要删除下一个节点
        if (nth == n) {
            p.next = p.next.next;
        }
        // 当前节点的倒数位置 nth + 1
        return nth + 1;
    }
}

Question:p.next.next 不怕空指针吗?

Answer:

  • p 是待删除节点的上一个节点,如果能递归回到 p,那么 p.next 肯定有值,不会是 null
  • 且题目说明了 n >=1,不会因为 nth == 0 而让 p.next 指向最后的 null


相关文章
|
20天前
|
存储 SQL 算法
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
|
16天前
|
Java Python
二刷力扣--链表
二刷力扣--链表
|
20天前
|
存储 算法 数据可视化
深入解析力扣160题:相交链表的解决方法(哈希表法与双指针法详细图解)
深入解析力扣160题:相交链表的解决方法(哈希表法与双指针法详细图解)
|
20天前
|
SQL 算法 数据可视化
LeetCode题目92:反转链表ll 【python 递归与迭代方法全解析】
LeetCode题目92:反转链表ll 【python 递归与迭代方法全解析】
|
21天前
|
存储 算法 数据挖掘
LeetCode 题目 43:字符串相乘 多种算法分析对比 【python】
LeetCode 题目 43:字符串相乘 多种算法分析对比 【python】
|
21天前
|
SQL 算法 数据挖掘
力扣题目 19:删除链表的倒数第N个节点 【python】
力扣题目 19:删除链表的倒数第N个节点 【python】
|
4天前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
4天前
|
C语言
指针进阶(回调函数)(C语言)
指针进阶(回调函数)(C语言)
|
4天前
|
存储 C语言 C++
指针进阶(函数指针)(C语言)
指针进阶(函数指针)(C语言)
|
4天前
|
编译器 C语言
指针进阶(数组指针 )(C语言)
指针进阶(数组指针 )(C语言)