leetcode
leetcode 2551 ~ 2600
移除重复节点

移除重复节点

难度:

标签:

题目描述

Write code to remove duplicates from an unsorted linked list.

Example1:

 Input: [1, 2, 3, 3, 2, 1]
 Output: [1, 2, 3]

Example2:

 Input: [1, 1, 1, 1, 2]
 Output: [1, 2]

Note:

  1. The length of the list is within the range[0, 20000].
  2. The values of the list elements are within the range [0, 20000].

Follow Up:

How would you solve this problem if a temporary buffer is not allowed?

代码结果

运行时间: 68 ms, 内存: 20.6 MB


/*
 * 思路:
 * 1. 将链表转换为流,去除重复的节点。
 * 2. 将结果转换回链表。
 */

import java.util.HashSet;
import java.util.LinkedList;
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 removeDuplicateNodes(ListNode head) {
        if (head == null) return head;
        List<Integer> values = new LinkedList<>();
        ListNode current = head;
        while (current != null) {
            values.add(current.val);
            current = current.next;
        }
        List<Integer> distinctValues = values.stream().distinct().collect(Collectors.toList());
        ListNode dummy = new ListNode(0);
        ListNode newCurrent = dummy;
        for (int value : distinctValues) {
            newCurrent.next = new ListNode(value);
            newCurrent = newCurrent.next;
        }
        return dummy.next;
    }
}

解释

方法:

该题解采用哈希表来存储已经出现过的节点的值,以此来检测重复的节点。首先判断如果链表为空或者只有一个节点,则直接返回链表头部。使用两个指针,pre 和 cur,分别表示当前节点的前一个节点和当前节点。pre 初始化为头节点,cur 初始化为头节点的下一个节点。遍历链表,并使用集合 s 存储已经访问过的节点值。在遍历过程中,如果 cur 指向的节点的值在集合 s 中,说明该值已存在,需要将其从链表中移除,即通过修改 pre 的 next 指针实现。如果 cur 指向的节点的值不在集合中,则将该值加入集合,并将 pre 指针移动到 cur。这个过程一直持续到链表末尾。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在实现中使用哈希表来存储节点值的主要优势是什么?为什么不选择其他数据结构如数组或链表?
使用哈希表来存储节点值的主要优势在于其高效的查找、插入和删除操作,通常这些操作的时间复杂度为O(1)。相比之下,如果使用数组存储节点值,则在查找一个值是否存在时的时间复杂度为O(n),因为可能需要遍历整个数组。使用链表存储节点值虽然插入操作较快(尤其是在链表头部插入),但查找和删除操作的时间复杂度同样为O(n),因为可能需要遍历链表来查找或删除一个节点。因此,哈希表在处理此类问题时,提供了更高的效率和更快的响应速度。
🦆
题解中提到如果链表元素在集合中,则将其移除,但具体如何处理节点的移除?例如,如果`pre.next`已经是`None`,这种情况下如何操作?
在题解中,如果发现当前节点`cur`的值已经存在于集合中,节点的移除是通过修改前一个节点`pre`的`next`指针来实现的,具体是将`pre.next`设置为`pre.next.next`。这样,`cur`节点就被从链表中断开,因此被实际移除。如果`pre.next`已经是`None`,这意味着已经到达链表的末尾,不会再有节点需要检查或移除,因此这种情况下不需要特别操作。
🦆
为什么在发现节点值重复时,只修改`pre.next`而不需要额外处理`cur`指针?
在移除重复节点时,修改`pre.next`足以从链表中断开当前节点`cur`,因此`cur`节点已经被移除。之后,代码中`cur`指针会更新为`pre.next`,即指向下一个待检查的节点。这样的处理确保了`cur`指针总是指向正确的当前节点,而不需要额外的操作来维护或重置`cur`指针。这种方法简化了逻辑并保持了代码的整洁性。
🦆
在题解的代码实现中,当链表只有一个元素时直接返回头结点,这种处理方式是否适用于所有可能的输入情况?
当链表只有一个元素时,由于没有其他节点,因此不可能存在重复的值。直接返回头节点是合理的,因为没有任何更改需要应用于链表。这种处理方式适用于所有单节点链表的情况,确保了算法的正确性和效率。

相关问题