leetcode
leetcode 2151 ~ 2200
从链表中移除节点

从链表中移除节点

难度:

标签:

题目描述

You are given the head of a linked list.

Remove every node which has a node with a greater value anywhere to the right side of it.

Return the head of the modified linked list.

 

Example 1:

Input: head = [5,2,13,3,8]
Output: [13,8]
Explanation: The nodes that should be removed are 5, 2 and 3.
- Node 13 is to the right of node 5.
- Node 13 is to the right of node 2.
- Node 8 is to the right of node 3.

Example 2:

Input: head = [1,1,1,1]
Output: [1,1,1,1]
Explanation: Every node has value 1, so no nodes are removed.

 

Constraints:

  • The number of the nodes in the given list is in the range [1, 105].
  • 1 <= Node.val <= 105

代码结果

运行时间: 312 ms, 内存: 57.9 MB


/*
题目思路:
1. 由于链表的单向特性,我们需要先将链表的节点值存入列表。
2. 通过列表的stream从右向左遍历,维护一个最大值,筛选出符合条件的值。
3. 将筛选出的值重新构建成链表。
*/

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}

public class Solution {
    public ListNode removeNodes(ListNode head) {
        List<Integer> values = new ArrayList<>();
        while (head != null) {
            values.add(head.val);
            head = head.next;
        }
        Collections.reverse(values);
        List<Integer> filteredValues = new ArrayList<>();
        int max = Integer.MIN_VALUE;
        for (int value : values) {
            if (value >= max) {
                max = value;
                filteredValues.add(value);
            }
        }
        Collections.reverse(filteredValues);
        ListNode dummy = new ListNode(0);
        ListNode current = dummy;
        for (int value : filteredValues) {
            current.next = new ListNode(value);
            current = current.next;
        }
        return dummy.next;
    }
}

解释

方法:

题解采用的是递归方法来解决问题。首先,判断当前节点是否为尾节点,如果是,则直接返回该节点。否则递归调用当前函数处理当前节点的下一个节点。在递归返回后,比较当前节点的值与其下一个节点的值。如果当前节点的值小于下一个节点的值,则说明当前节点应该被移除,因此返回下一个节点。如果不是,则保留当前节点,返回当前节点。这样递归处理可以确保从链表末尾开始逐个检查节点,每次递归返回时都能决定是否保留当前节点。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在递归解决方案中,如何确保递归栈不会因为链表过长而溢出?
在使用递归解决链表问题时,确保递归栈不溢出的主要方法是限制链表的长度或使用尾递归优化。然而,Python 并不默认支持尾递归优化,因此对于极长的链表,递归解决方案可能会导致栈溢出错误。为避免这种情况,可以考虑改用迭代方法处理链表,这种方法不依赖调用栈,因此更适合处理长链表。
🦆
递归解决方案中,当链表长度极大时,会存在性能问题吗?如果有,如何优化?
递归解决方案中,每次递归调用都会增加堆栈的深度,导致大量资源消耗,尤其是当链表长度极大时,这会导致性能问题甚至栈溢出。为优化这一问题,可以转而使用迭代方法,该方法通过循环而非递归来遍历链表,从而有效控制资源消耗。此外,也可以考虑使用非递归的栈结构手动模拟递归过程,以减少系统调用栈的使用。
🦆
递归返回时,若当前节点值小于下一个节点值,则移除当前节点。在这种情况下,被移除的节点的内存如何处理?是否需要考虑内存泄露问题?
在Python中,当一个对象(如链表中的节点)不再被引用时,它将由Python的垃圾收集器自动回收。因此,当递归函数将一个节点的引用改为指向其他节点时,如果没有其他引用指向被移除的节点,该节点将自动成为垃圾收集的目标。不过,在一些没有自动垃圾收集的语言中,确实需要手动管理内存,如调用适当的函数来释放被移除节点的内存,以避免内存泄露。
🦆
题解中没有处理所有节点相等的情况的特殊逻辑,这是否意味着算法自然支持这种情况?如果是,为什么?
题解中的算法自然支持所有节点值相等的情况。这是因为算法的核心逻辑是比较当前节点与下一个节点的值,只有当当前节点的值小于下一个节点的值时才移除当前节点。如果所有节点值相等,这一条件从不满足,因此所有节点都会被保留。这意味着算法无需特殊处理即可正确处理所有节点值相同的情况。

相关问题