每日一题——力扣26. 删除有序数组中的重复项(举一反三)

34f0aab6124a400bb321dd38682233c3.jpeg

一个认为一切根源都是“自己不够强”的INTJ

b450501d80dc4e5e820b4403e45bc84a.png个人主页:用哲学编程-CSDN博客
b450501d80dc4e5e820b4403e45bc84a.png​专栏:每日一题——举一反三

目录

我的写法:

代码点评:

时间复杂度分析:

空间复杂度分析:

总结:

我要更好!

这种方法体现了以下哲学和编程思想:

举一反三!


 

题目链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-array/description/
d7ef6b4178764c31b0c6a977ee54f541.png

我的写法:

// 定义一个函数,用于移除数组中的重复元素,并返回新数组的长度
int removeDuplicates(int* nums, int numsSize) {
    // 如果数组只有一个元素,那么它没有重复,直接返回1
    if(numsSize==1)
    {
        return 1;
    }
    // 初始化变量:i 用于记录不重复元素的位置,j 用于遍历数组,xor 用于异或操作,counts_re 用于记录重复元素的数量
    int i,j,xor,counts_re;
    // 初始化循环变量,xor 初始化为数组的第一个元素,counts_re 初始化为0
    for(i=0,j=1,xor=nums[0],counts_re=0;j<numsSize;j++)
    {
        // 对当前元素和 xor 进行异或操作,xor 初始为前一个元素的值
        xor^=nums[j];
        // 如果异或结果为0,说明当前元素与前一个元素相同,计数器 counts_re 增加
        if(xor==0)
        {
            counts_re++;
        }else{
            // 如果异或结果不为0,说明当前元素与前一个元素不同,将当前元素移动到 i 指向的位置,并增加 i
            i++;
            nums[i]=nums[j];
        }
        // 更新 xor 为当前元素的值,以便下一次循环使用
        xor=nums[j];
    }
    // 返回原数组长度减去重复元素的数量,即新数组的长度
    return numsSize-counts_re;
}

这段代码的目的是移除数组中的重复元素,并返回新数组的长度。下面是对这段代码的专业点评,包括时间复杂度和空间复杂度的分析:

代码点评:

  1. 逻辑清晰:代码的逻辑是清晰的,通过异或操作来判断相邻元素是否相同,这是一种有效的比较方法。
  2. 边界条件处理:代码考虑了数组只有一个元素的情况,并正确处理了这种边界条件。
  3. 双指针技术:使用 i 和 j 两个指针来遍历数组,i 用于记录不重复元素的位置,j 用于遍历整个数组,这是一种常见的双指针技术。
  4. 重复计数:通过 counts_re 变量来记录重复元素的数量,这是一种有效的计数方法。
  5. 原地修改:代码在原数组上进行修改,没有使用额外的数组空间,这有助于节省内存。

时间复杂度分析:

  • 时间复杂度:这段代码的时间复杂度是 O(n),其中 n 是数组的长度。这是因为代码使用了一个循环来遍历整个数组,每个元素只被访问一次。

空间复杂度分析:

  • 空间复杂度:这段代码的空间复杂度是 O(1),即常数级别的额外空间使用。这是因为代码只使用了几个额外的变量(i、j、xor、counts_re),并没有使用与输入规模成比例的额外空间。

总结:

这段代码是一个有效的方法,用于移除数组中的重复元素,并返回新数组的长度。它的时间复杂度是 O(n),空间复杂度是 O(1),表明它是一个高效的解决方案。不过其中的变量xor是多余的。
 

我要更好!

要去除 xor 变量并保持代码功能不变,可以直接比较当前元素 nums[j] 和它前一个元素 nums[i]。当两者不相等时,应当将 nums[j] 移动到 nums[i+1] 的位置,并增加 i 的值,这样可以保持 nums[i] 始终指向最新的不重复元素的位置。

修改后的代码:

int removeDuplicates(int* nums, int numsSize) {
    // 如果数组大小小于或等于1,它要么为空,要么已经没有重复元素
    if (numsSize <= 1) return numsSize;

    // i 用于记录最新的不重复元素的位置
    int i = 0; // 从第一个元素开始,i 指向最后一个不重复元素的位置

    for (int j = 1; j < numsSize; j++) {
        // 当前元素与前一个不重复的元素比较
        if (nums[j] != nums[i]) {
            // 当发现不重复的元素时,移动到i的下一个位置
            i++;
            nums[i] = nums[j];
        }
    }

    // 新数组长度为最后一个不重复元素的索引 + 1
    return i + 1;
}

这个版本,去掉了 xor 变量,这样做的好处是简化了逻辑,同时避免了对异或操作的不必要依赖。原本的 xor 逻辑更多是为了理解异或操作如何工作,而在实际应用中直接比较相邻元素通常更直接和易于理解。这个改进的版本仍旧遵循 O(n) 的时间复杂度和 O(1) 的空间复杂度,适用于大多数需要去重复元素的场景。

这种方法体现了以下哲学和编程思想:

  1. 简洁性(Simplicity):通过去除不必要的变量(如 xor),代码变得更加简洁和易于理解。这种简洁性是编程中的一个重要原则,因为它有助于减少错误和提高代码的可维护性。
  2. 增量式开发(Incremental Development):代码逐步构建,每次只处理一个元素,确保每个步骤都是正确的。这种增量式的方法有助于在开发过程中及时发现和纠正问题。
  3. 迭代(Iteration):使用循环(for 循环)来遍历数组中的每个元素,这是编程中处理重复任务的常用方法。迭代是编程中的一个基本概念,它允许程序重复执行相同的代码块,直到满足某个条件。
  4. 抽象(Abstraction):通过函数 removeDuplicates 将去重逻辑封装起来,使得调用者不需要关心具体的实现细节,只需要知道函数的功能和输入输出。这种抽象有助于提高代码的可读性和可重用性。
  5. 单一职责原则(Single Responsibility Principle):函数 removeDuplicates 只负责一个任务,即移除数组中的重复元素。这种单一职责原则有助于保持代码的清晰和模块化。
  6. 空间和时间的权衡(Space-Time Tradeoff):虽然原始代码使用了一个额外的变量 xor,但修改后的代码避免了这种空间上的开销,同时保持了时间复杂度为 O(n)。这种权衡是算法设计中的一个常见考虑因素。
  7. 函数式编程思想(Functional Programming):虽然这段代码是命令式的,但它体现了函数式编程中的一些思想,如避免副作用(函数内部的操作不改变输入数组以外的状态)和使用纯函数(给定相同的输入,总是产生相同的输出)。
  8. 实用主义(Pragmatism):代码的目的是解决问题,而不是追求理论上的完美。在这个例子中,通过实际的比较操作来移除重复元素,而不是使用理论上可能更优雅但实际应用中可能更复杂的解决方案。


举一反三!
 

根据移除数组中重复元素的方法,这里提供一些编程技巧,帮助您在其他场景下举一反三:

  1. 理解问题的本质:在开始编码之前,花时间搞清楚问题的实质。去除数组重复元素的问题的本质是识别和保留唯一的元素,这个理解帮助简化解决方案。
  2. 预设条件优化:利用已知的条件(如排序数组)来简化算法。在排序数组中去除重复元素更容易,因为重复项总是连续出现的。
  3. 修改原地:尽可能地原地修改数组,这样可以节省额外的内存空间。在处理大数据量时,原地算法非常有用。
  4. 双指针技巧:使用两个指针在一个循环中分别追踪不同的信息。在本例中,一个指针追踪不重复序列的末尾,另一个追踪当前读到的元素。
  5. 不怕简单:有时候最简单的解决方案就是最佳方案。不要过度设计,避免引入不必要的复杂性。
  6. 避免不必要的操作:在可能的情况下,避免不必要的计算和比较,这可以通过直接比较相邻元素来实现,而不是采用更复杂的算法。
  7. 重构代码:重构代码以提高可读性和性能。即使你的第一个解决方案是有效的,通过重构,你可能会发现一个更优雅或更快的方法。
  8. 递增索引管理:在处理数组和列表时,理解如何使用递增索引来有效地管理元素位置和遍历。
  9. 测试边界条件:当编写函数时,要考虑并测试边界条件,例如空数组、只有一个元素的数组或所有元素都相同的数组。
  10. 编写可重用代码:将通用的解决方案抽象成函数或模块,以便在不同的问题中重用它们,提高代码的可复用性。

本节内容到此结束!!感谢阅读!

 

  • 22
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

用哲学编程

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

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

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

打赏作者

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

抵扣说明:

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

余额充值