删除链表的倒数第N个节点-链表19-python&c++

算法思想

一、暴力法:

没看答案

  1. 由于删除倒数第n个节点就等同于将链表反转后删除正数第n个节点,所以先将链表反转;
  2. 然后删除正数第n个节点;
  3. 再将删除操作后的链表顺序反转,即返回到最初链表的顺序。
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head, n):
        head = self.ReverseLinkedList(head)
        head = self.RemoveNthFromBeginning(head, n)
        head = self.ReverseLinkedList(head)
        return head
    
    def ReverseLinkedList(self, head):
        pre = None
        while(head):
            temp = head.next
            head.next = pre
            pre = head
            head = temp
        return pre

    def RemoveNthFromBeginning(self, head, n):
        assert head != None
        head_new = head
        if n == 1:
            head = head.next
            head_new = head
        else:
            for i in range(n-1):
                if i == n - 2:
                    head.next = head.next.next
                else:
                    head = head.next
        return head_new

复杂度分析:

  • 时间复杂度:O(L),其中 L 是链表的长度。
  • 空间复杂度:O(1)。

二、快慢指针

  • 由于我们需要找到倒数第 n 个节点,因此我们可以使用两个指针 fast 和 slow 同时对链表进行遍历,并且 fast 比 slow 超前 n 个节点。当 fast 遍历到链表的末尾时,slow 就恰好处于倒数第 n 个节点。
  • 具体地,初始时 fast和 slow 均指向头节点。我们首先使用 fast 对链表进行遍历,遍历的次数为 n。此时,fast 和 slow 之间间隔了 n−1 个节点,即 fast 比 slow 超前了 n 个节点。
  • 在这之后,我们同时使用 fast 和 slow 对链表进行遍历。当 fast 遍历到链表的末尾(即 fast为空指针)时,slow 恰好指向倒数第 n 个节点。
  • 根据方法一和方法二,如果我们能够得到的是倒数第 n 个节点的前驱节点而不是倒数第 n 个节点的话,删除操作会更加方便。因此我们可以考虑在初始时将 slow 指向哑节点,其余的操作步骤不变。这样一来,当 fast 遍历到链表的末尾时,slow 的下一个节点就是我们需要删除的节点。
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head, n):
        pre = ListNode(100, head)
        slow = pre
        fast = head
        for i in range(n):
            fast = fast.next
        while(fast):
            fast = fast.next
            slow = slow.next
        slow.next = slow.next.next

        return pre.next

复杂度分析:

  • 时间复杂度:O(L),其中 L 是链表的长度。
  • 空间复杂度:O(1)。

C++

快慢指针

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        // 快慢指针初始化在哨兵节点,快指针先于慢指针走n步,然后再相同速度走,直到快指针为空,此时慢指针指向的节点为待删除结点
        // 快慢指针走的同时,需要记录下慢指针前一个节点
        // 使用哨兵节点,是因为链表长度为1时,不需要再单独判断了
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        ListNode* fast = dummy, *slow = dummy;
        ListNode* before = nullptr;
        for (int i = 0; i < n; i++) {
            fast = fast->next;
        }

        while (fast != nullptr) {
            fast = fast->next;
            before = slow;
            slow = slow->next;
        }
        before->next = slow->next;
        slow->next = nullptr;

        return dummy->next;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值