每日一题-删除有序链表中重复的元素-II

38 篇文章 0 订阅
32 篇文章 0 订阅

题目描述

给定一个升序排序的链表,删除链表中的所有重复出现的元素,只保留原链表中只出现一次的元素。

示例

示例 1
输入:

1 → 2 → 3 → 3 → 4 → 4 → 5

返回:

1 → 2 → 5

示例 2
输入:

1 → 1 → 1 → 2 → 3

返回:

2 → 3

数据范围

  • 链表长度: 0 ≤ n ≤ 10000 0 \leq n \leq 10000 0n10000
  • 链表中的值满足 ∣ v a l ∣ ≤ 1000 |val| \leq 1000 val1000

要求

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

进阶

  • 空间复杂度: O ( 1 ) O(1) O(1)
  • 时间复杂度: O ( n ) O(n) O(n)

解题思路

我们需要遍历链表并删除所有重复出现的元素。由于链表是升序排序的,因此可以利用这个特点进行优化,处理的方式是利用两个指针(precur)遍历链表,pre指向处理后的最后一个节点,cur指向当前遍历的节点。

具体的做法是:

  1. 当当前节点的值不等于下一个节点的值时,将pre指向当前节点,cur指向下一个节点。
  2. 当当前节点的值等于下一个节点的值时,跳过所有重复的节点,直到找到一个新的不同值的节点,并将prenext指向这个节点。

这种方法可以实现一次遍历完成,时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)(不考虑递归栈的空间)。

代码实现

/**
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

/**
 * 删除有序链表中重复的元素,只保留出现一次的元素
 *
 * @param head ListNode类
 * @return ListNode类
 */
struct ListNode* deleteDuplicates(struct ListNode* head) {
    // 如果链表为空或只有一个元素,直接返回
    if (head == NULL || head->next == NULL)
        return head;

    // 创建一个虚拟头节点,方便处理头节点的删除
    struct ListNode* dummy = (struct ListNode*) malloc(sizeof(struct ListNode));
    dummy->next = head;
    struct ListNode* pre = dummy;  // pre指向虚拟头节点
    struct ListNode* cur = head;   // cur指向当前节点

    while (cur != NULL) {
        // 如果当前节点的值不等于下一个节点的值,正常移动
        if (cur->next == NULL || cur->val != cur->next->val) {
            pre = pre->next;  // 更新pre
            cur = cur->next;  // 更新cur
        } else {
            // 如果当前节点的值等于下一个节点的值,跳过重复节点
            while (cur->next != NULL && cur->val == cur->next->val) {
                cur = cur->next;  // 跳过所有重复节点
            }
            pre->next = cur->next;  // 将pre的next指向当前节点的下一个节点
            cur = cur->next;        // 更新cur
        }
    }

    return dummy->next;  // 返回去除虚拟头节点后的链表
}

代码解释

  1. 链表为空或只有一个元素时,直接返回原链表,因为不需要进行任何处理。

  2. 创建虚拟头节点dummy节点用于处理链表的头节点情况。pre指向虚拟头节点,cur指向链表的第一个节点。

  3. 遍历链表

    • 如果当前节点与下一个节点的值不同,说明当前节点没有重复值,我们可以将pre更新为cur,然后移动cur到下一个节点。
    • 如果当前节点与下一个节点的值相同,说明有重复节点,我们需要跳过所有相同的节点,将pre->next指向当前节点的下一个不同值的节点,直到找到新的值。
  4. 返回虚拟头节点的next指针,即去除重复元素后的链表。

复杂度分析

  • 时间复杂度:每个节点最多被访问两次(一次正常遍历,另一次跳过重复节点),因此时间复杂度为 O ( n ) O(n) O(n),其中 n n n是链表的长度。

  • 空间复杂度:除了返回的链表外,我们只用了常数空间(dummy, pre, cur)。因此,空间复杂度为 O ( 1 ) O(1) O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tt555555555555

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值