力扣 19 删除链表的倒数第 N 个结点

题目链接

https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/

源代码 github 地址

https://github.com/YIMEng-0/DataStructure

1、题目要求

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点

2、思路分析

使用双指针法实现倒数第 n 个元素的删除,快指针首先移动 n 位;

移动结束后(此时 慢指针指向虚拟头结点,快指针指向的是第 n 个元素),接下来慢指针和快指针同步移动,每次两个指针都移动一个单位;

这个时候,当快指针移动到链表的 null 的时候,慢指针刚好指向待删除元素,也就是slow 指针指向了倒数第 n 个元素;

将慢指针的前一个元素进行保留,创建指针 ListNode prev = slow;

最后进行链表的连接,slow 指针的前一个元素连接到 slow 指针的下一个元素,实现了将 slow 指针指向的元素的删除,链表删除元素就是找到待删除元素,让待删除元素的前一个结点指向待删除元素的后一个结点,待删除元素没有指针指向它,会被垃圾回收器回收;
prev.next = slow.next

3、执行代码

package com.luobin.力扣题目.力扣_链表.删除倒数第n个元素;

/**
 * @author Doraemon
 * @date 2022/1/13 6:23 下午
 * @version 1.0
 */
class ListNode {
    int val;
    ListNode next;

    ListNode() {
    }

    ListNode(int val) {
        this.val = val;
    }

    ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

/**
 * 题目概述:
 *    给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = head;
        ListNode fast = dummyHead;
        ListNode slow = dummyHead;

        // 首先让fast 指针移动 n 个位置,方便进行元素的删除处理
        while (n != 0) {
            fast = fast.next;
            n = n - 1;
        }
        /**
         上面的  while 循环可以使用下面的第二种方式:
         while(n-- > 0) {
         fast = fast.next;
         }

         两个循环都是为了让 fast 指针进行移动 n 次;说白了就是控制循环的次数
         */

        // 定义一个临时结点,用来保存 slow 结点的上一个结点
        ListNode prev = null;
        // 这样子可以进行链表的连接操作, slow 的上一个结点连接到 slow 的下一个结点,刚好把 slow 指向的当前结点删除掉
        while (fast != null) {
            prev = slow;
            slow = slow.next;
            fast = fast.next;
        }

        // 位置找到了,进行链表的连接,删除 slow 指向的元素
        prev.next = slow.next;
        return dummyHead.next;
    }
}

4、问题反思

4.1、对于 while 循环里面的条件进行理解

假设 n = 4
        while (n != 0) {
        	sout(n);
            fast = fast.next;
            n = n - 1;
        }
 输出 4 3 2 1 
假设 n = 4
  	    while(n-- > 0) {
  	        sout(n);
            fast = fast.next;
        }
 输出 3 2 1 0 

4.2、为什么快指针一定要移动 n 次先?

  • 这个双指针发的核心是让快指针进行一次链表的全部遍历,快指针先自己独自移动了 n 次;

  • 然后和慢指针再一起移动,直到快指针指向 null 的时候,

    也就是这个时候快指针实现了链表的全遍历,
    遍历的次数就是:
    n + 和慢指针一起移动的次数 + 1 = 链表的长度 + 1
    n ( n 是待删除的倒数第几个元素) + 和慢指针一起移动的次数 + 1 (多加了 1 是因为从虚拟头节点开始移动,当移动链表长度 + 1的时候,快指针才能指向空)

    快指针指向了空,此时慢指针指向的刚好就是待删除元素;

4.3 为什么当快指针指向 null 的时候,慢指针刚好指向倒数第 n 个元素?

在一开始的时候,先让快指针移动 n 的单位,此时快指针和慢指针之间存在的元素是 n - 1 个(不包括快指针和慢指针)个;当快指针移动到 null 的时候,慢指针和快指针之间的距离是不变的,一直是 n - 1,此时从快指针指向的 null 倒着向前面数 n - 1 个间距之后, slow 指向的就是倒数第 n 个元素;

5、小结

通过了双指针方法进行倒数第 n 个元素的删除,首先需要确定快指针先移动了多少次,这个循环怎么写,怎么控制快指针移动 n 次,这个很重要;使用了虚拟头结点可以使得对于链表头部的结点进行统一的处理;

posted @   YIMENG-0  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示