欢迎光临
我们一直在努力

【LeetCode每日一练】删除链表的倒数第 N 个结点|C语言快慢指针解法

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

示例1:

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

示例2:

输入:head = [1], n = 1 输出:[]

示例3:

输入:head = [1,2], n = 1 输出:[1]

进阶:你能尝试使用一趟扫描实现吗?


解题思路

核心:快慢指针 + 虚拟头结点dummy

  • 虚拟头结点dummy:解决要删除的节点就是头节点的边界情况。如果不使用虚拟头,删除头结点需要单独写分支判断,dummy统一所有删除逻辑。
  • 快慢指针思想
    • fast指针先向前走n步;
    • 之后fast、slow指针一起向后移动,直到fast->next == NULL;
    • 此时slow正好停在待删除节点的前驱结点;
    • 修改slow->next = slow->next->next,跳过目标节点,完成删除。
    • 返回dummy->next作为新链表头。

    原理图解

    以链表 1→2→3→4→5,n=2举例

  • dummy指向头节点,fast、slow都指向dummy。
  • fast先走2步,fast走到节点3。
  • fast和slow同步往后走,直到fast->next为NULL: fast走到5,slow停在节点3。
  • slow->next原本是4,执行slow->next = slow->next->next,直接把4节点跳过,链表变成1→2→3→5。

  • C语言完整代码

    /**
    * Definition for singly-linked list.
    * struct ListNode {
    * int val;
    * struct ListNode *next;
    * };
    */
    struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
    // 建立虚拟头结点,处理删除头节点边界
    struct ListNode dummy;
    dummy.next = head;

    struct ListNode* fast = &dummy;
    struct ListNode* slow = &dummy;

    // fast指针先走n步
    for(int i = 0; i < n; i++){
    fast = fast->next;
    }

    // fast和slow一起移动,直到fast的下一个为空
    while(fast->next != NULL)
    {
    fast = fast->next;
    slow = slow->next;
    }

    // 删除slow后面的节点
    slow->next = slow->next->next;

    return dummy.next;
    }

    关键坑点解析

    坑1:为什么一定要虚拟头结点dummy?

    测试用例 head=[1], n=1:需要删除第一个(也就是唯一的)节点。 如果没有dummy,slow没有前驱节点,删除头结点逻辑要单独写if判断。 引入dummy虚拟头,所有删除场景逻辑统一,不需要特殊分支。

    坑2:循环条件为什么是 fast->next != NULL,不是 fast != NULL

    • 如果写while(fast != NULL),slow最终会走到待删除节点本身,我们拿不到前驱,无法完成链表跳过操作。
    • fast->next==NULL停止循环,slow定位到待删节点的前一个节点,才可以修改next指针。

    坑3:内存泄漏问题(LeetCode判题不会卡,但工程代码要注意)

    上面代码做题可以直接AC,但是没有释放被删除节点的内存。 工程开发,删除节点建议补充释放内存:

    struct ListNode* delNode = slow->next;
    slow->next = slow->next->next;
    free(delNode);

    LeetCode OJ不校验内存泄漏,刷题可以省略;写项目务必加上free。


    补充:普通两次遍历的思路(对比)

  • 第一遍遍历链表,统计链表总长度L;
  • 需要删除的是正数第 L-n 位置节点;
  • 第二遍遍历走到前驱节点,执行删除。
  • 缺点:需要扫描链表两次;快慢指针方案只需要一趟遍历,满足题目的进阶要求。

    总结

  • 涉及链表可能删除头节点的场景,优先想到虚拟头结点dummy,简化边界;
  • 求链表倒数第k个节点经典模板:快慢指针,fast先走n步,再同步移动;
  • while循环条件注意观察终止位置,要拿到待删节点的前驱结点,而不是待删节点本身。
  • 原题链接:19. 删除链表的倒数第 N 个结点 – 力扣(LeetCode)

    赞(0)
    未经允许不得转载:171主机测评 » 【LeetCode每日一练】删除链表的倒数第 N 个结点|C语言快慢指针解法
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址