删除排序链表中的重复元素:三种解法详解
问题描述
给定一个已排序的链表的头节点 head
,要求删除所有重复元素,使得每个元素只出现一次。返回处理后的已排序链表。下面是leetcode题目如果感兴趣可以尝试一下。
方法一:递归解法
思路
递归法通过维护一个 ArrayList
来记录已遍历过的节点值。具体步骤如下:
-
将当前节点的值加入列表。
-
递归处理下一个节点:
-
如果下一个节点的值已存在于列表中,则跳过该节点(修改前驱节点的指针)。
-
否则,继续递归处理。
-
class Solution {public ListNode deleteDuplicates(ListNode head) {if (head == null) return head;ArrayList<Integer> numbers = new ArrayList<>();numbers.add(head.val);deletenode(head, head.next, numbers);return head;}private void deletenode(ListNode pre, ListNode node, ArrayList<Integer> numbers) {if (node == null) return;if (numbers.contains(node.val)) {pre.next = node.next; // 删除当前节点deletenode(pre, node.next, numbers); // 继续处理下一个节点} else {numbers.add(node.val);deletenode(node, node.next, numbers); // 更新前驱节点}}
}
复杂度分析
-
时间复杂度:O(n²),因为
ArrayList.contains()
的时间复杂度为 O(n)。 -
空间复杂度:O(n),递归调用栈和列表的空间。
缺点
-
递归深度可能达到链表长度,导致栈溢出。
-
重复值检查效率低。
方法二:迭代解法
思路
通过迭代遍历链表,使用 ArrayList
记录已存在的值。遇到重复值时,直接修改前驱节点的指针。
class Solution {public ListNode deleteDuplicates(ListNode head) {if (head == null) return head;ArrayList<Integer> numbers = new ArrayList<>();numbers.add(head.val);ListNode pre = head;ListNode node = head.next;while (node != null) {if (numbers.contains(node.val)) {pre.next = node.next; // 跳过重复节点} else {numbers.add(node.val);pre = node; // 更新前驱节点}node = node.next; // 移动指针}return head;}
}
复杂度分析
-
时间复杂度:O(n²),
ArrayList.contains()
的线性检查导致性能瓶颈。 -
空间复杂度:O(n),存储已遍历值的列表。
缺点
-
空间复杂度较高,不适用于大规模数据。
方法三:直接遍历(最优解)
思路
利用链表已排序的特性,只需比较相邻节点的值:
优点
-
若当前节点与下一节点的值相同,则跳过下一节点。
-
否则,移动指针继续遍历。
class Solution {public ListNode deleteDuplicates(ListNode head) {if (head == null) return head;ListNode cur = head;while (cur.next != null) {if (cur.val == cur.next.val) {cur.next = cur.next.next; // 删除重复节点} else {cur = cur.next; // 移动指针}}return head;} }
复杂度分析
-
时间复杂度:O(n),只需一次遍历。
-
空间复杂度:O(1),无需额外空间。
-
高效且节省内存,完美利用链表有序的特性。
方法对比
方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
---|---|---|---|
递归解法 | O(n²) | O(n) | 小规模数据,不推荐实际使用 |
迭代+ArrayList | O(n²) | O(n) | 未排序链表,但性能较差 |
直接遍历 | O(n) | O(1) | 已排序链表,最优选择 |
总结
对于已排序链表,方法三是最优解,其时间复杂度和空间复杂度均为最优。递归法和迭代法虽然逻辑简单,但在处理大规模数据时性能不足。建议根据链表是否有序选择合适的算法。