删除链表的倒数第 n 个节点

217 阅读2分钟

题目描述

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。 示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.

说明: 给定的 n 保证是有效的。

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

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list

解题思路

删除链表中指定节点的通常做法是:遍历到相应的位置,并修改前一个节点的 next 指针指向被删除节点的下一个节点,如下图所示。

本题要求删除倒数第 n 个节点,因此需要知道链表的长度 l,用 l-n 即为节点所在的位置。

常规的做法是两次遍历:

  • 第一遍先遍历链表长度

  • 第二遍遍历到指定的节点,然后删除

但本题目要求使用一次遍历,因此需要使用一些技巧。

双指针单次遍历的思路:

  • 利用两个指针 p1,p2,从左到右依次遍历,p1 在前,p2 在后。

  • p1 先向后移动 n 分节点,两个指针之间的差距是 n。

  • 当 p1 指针遍历到结尾时,p2 正好指向被删除的节点的位置。

  • 进而可以删除链表的倒数第 n 个节点。

解题代码

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        //构造一个虚拟节点,指向链表头节点
        ListNode* h1 = new ListNode(0);
        h1->next = head;
        ListNode* p1 = h1;//p1->next指向链表的头节点
        ListNode* p2 = h1;//p2->next指向链表的头节点
        
        //p1指针先走n个节点
        for (int i = 0; i < n ; i++ )
            p1 = p1->next;

        //p1,p2向后同时移动,直到指向最后一个节点
        while (p1->next != NULL)
        {
            p1 = p1->next;
            p2 = p2->next;
        }
        //此时p2指向的是被删除节点的前一个节点
        p2->next = p2->next->next;

        //返回链表的头节点
        return h1->next;
    }
};

时间复杂度: O(L) , L 为链表长度 空间复杂度: O(1)

分析总结

  • 创建一个虚拟节点,指向头结点,这样做可以回避掉如果链表只有一个结点,需要单独处理的情况。

  • 删除一个结点时,需要定位到这个待删除结点的前驱结点,所以要使 p2 指向待删除结点的前驱,而不是指向待删除结点本身。

本文转载软件工程实践领航营