打败算法 —— 删除链表的倒数第n个结点

本文参考

出自LeetCode上的题库 —— 删除链表的倒数第n个结点,官方的双指针解法没有完全符合"只遍历一遍链表"的要求,本文给出另一种双指针解法

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

删除倒数结点问题

给你一个链表,删除链表的倒数第 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]

解题思路

第一种解法,先遍历一遍原列表,记录链表长度,然后再遍历一遍就能够删除对应的结点

第二种解法利用双指针,我们"回文链表"(https://www.cnblogs.com/kuluo/p/15955593.html)中通过双指针找到中间节点(slow指针所在的位置),因此也能够计算链表的长度。获取到链表的长度后,根据倒数第n个结点是在slow指针前还是在slow指针后,决定是从head头节点开始找,还是从slow指针所指的结点开始找,相当于减少了一半的结点遍历过程

在这道题中,我们看到双指针不但能够使算法保持在$O(n)$的时间复杂度,也能将空间复杂度限制在常数级$O(1)$

双指针解法

class ListNode:
  def __init__(self, val=0, next=None):
    self.val = val
    self.next: ListNode = next
class Solution:
  def remove_Nth_from_end(self, head: ListNode, n: int) -> ListNode:
    cnt = 1
    slow = fast = head

    # 通过快慢指针获得链表长度 

    while fast.next and fast.next.next:
      cnt += 1
      slow = slow.next
      fast = fast.next.next

    # 计算链表长度 

    if fast.next is None:
      length = cnt * 2 - 1
    else:
      length = cnt * 2

    # 特殊情况 

    if n == length:
      return head.next

    # 链表的前半部分 
        # length = 5, n = 2, step = 5 - 2 - 3 + 1 = 1
    if
n > length / 2:
      step = length - n
      curr = pre = head
    # 链表的后半部分 

    else:
      step = length - n - cnt + 1
      curr = pre = slow

    # 获得步长后移动到特定位置并删除指针 

    for i in range(step):
      pre = curr
      curr = curr.next
    curr = curr.next
    pre.next = curr

    return head

posted @ 2022-03-04 19:53  咕~咕咕  阅读(38)  评论(0编辑  收藏  举报