19. Remove Nth Node From End of List (双指针)

本文介绍了一种高效算法,用于在一次遍历中删除链表倒数第N个节点。通过使用快慢指针技巧,当快指针到达链表尾部时,慢指针恰好位于待删除节点的前一个位置,从而实现O(n)时间复杂度内的操作。

Given a linked list, remove the n-th node from the end of list and return its head.

Example:

Given linked list: 1->2->3->4->5, and n = 2.

After removing the second node from the end, the linked list becomes 1->2->3->5.

Note:

Given n will always be valid.

Follow up:

Could you do this in one pass?

分析:

要在一遍内执行完的问题就是指针不能知道当前节点是倒数第几个,所以考虑用快慢指针。快指针比慢指针快n个节点,当快指针的下一个元素为空就说明慢指针的下一个节点应该删除。

注意:

1. 如果链表本来就只有一个节点,删除之后就是空,所以不能返回head,而是应该new一个新的节点指向head,返回新节点的next;

 public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode sta = new ListNode(0);
        ListNode slow = sta;
        ListNode fast = sta;
        slow.next = head;
        for(int i=0;i<n;i++)
            fast = fast.next;
        while(fast.next!=null){
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return sta.next;
    }

 

好的,我们可以通过数组和简单的循环来解决“删除链表的倒数第 \(N\) 个节点”这个问题。相比传统的双指针解法,这种方法更直观且易于理解。下面我会详细讲解如何一步步解决问题。 --- ### 解题思路 1. **将链表转化为数组** 我们可以用一个数组存储链表的所有节点值。这使得我们可以直接通过索引来访问任意节点,而不需要复杂的指针操作。 2. **确定要删除的目标节点** 假设数组长度为 `length`,那么我们需要删除的倒数第 \(k\) 个节点对应于正序遍历下的 `(length - k)` 索引处的节点。 3. **重构新的链表** 根据上述索引信息,在跳过目标节点的情况下,重新构造一个新的链表。 --- ### 具体步骤说明 #### 第一步:将链表存入数组 我们用一个空数组来依次保存所有节点的值。比如对于输入 `[1 -> 2 -> 3 -> 4 -> 5]`,我们会得到数组 `[1, 2, 3, 4, 5]`。 #### 第二步:计算目标节点的位置 假设需要删除倒数第 \(k\) 个节点,则其对应的正序索引应为 `index = length - k`(注意这里是从零开始计数)。例如,如果 \(k=2\) 并且总共有 5 个节点,那么目标索引就是 `5 - 2 = 3`,也就是数值为 4 的那个节点。 #### 第三步:排除目标节点后重建链表 从头到尾扫描一次数组,在遇到目标索引时将其忽略,并按顺序连接剩余部分形成新链表。 --- ### 示例代码 (Python 版本) 以下是完整的 Python 实现代码: ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def remove_nth_from_end(head, k): # Step 1: Convert the linked list to an array of values. arr = [] current = head while current: arr.append(current.val) # Store each value into the array current = current.next # Step 2: Find out which element should be removed based on index calculation. length = len(arr) target_index = length - k # Target index in normal order. # Edge case check when deleting exceeds bounds or invalid inputs are given. if not (0 <= target_index < length): return head # Invalid request; no action taken. # Step 3: Rebuild a new linked list excluding that specific position's item. dummy_head = tail_ptr = ListNode(-1) # Dummy placeholder at front makes life easier later during construction phase only! for i in range(length): if i != target_index: tail_ptr.next = ListNode(arr[i]) # Create fresh nodes except ones marked as skipped over already earlier within loop iterations thus far.. tail_ptr = tail_ptr.next # Move forward onto newly created node just appended right before moving further down again recursively until completion finally happens after all elements have been traversed through completely now... return dummy_head.next # Skip past initial fake header marker set up originally previously above here.... # Helper function to convert input strings back towards proper formated outputs easily readable visually then... def build_linked_list(values): dummy = curr = ListNode(-1) for v in values: curr.next = ListNode(v) curr = curr.next return dummy.next def print_linked_list(head): vals = [] while head: vals.append(str(head.val)) head = head.next return " ".join(vals) if __name__ == "__main__": from sys import stdin lines = [line.strip() for line in stdin] n = int(lines[0]) # Number of total items present inside original sequence provided initially by user query prompt etcetera..!? nums = list(map(int, lines[1].split())) k = int(lines[2]) llist = build_linked_list(nums[:n]) ans_llist = remove_nth_from_end(llist, k) output = print_linked_list(ans_llist) print(output) ``` --- ### 测试例子 假如我们的输入数据如下所示: ``` Input: 5 1 2 3 4 5 2 Output: 1 2 3 5 ``` 程序运行结果应该是正确的去除掉了原本存在于第四位上的数字"4". ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值