算法practice1

63次阅读
没有评论

 

https://github.com/CyC2018/CS-Notes/blob/master/notes/3.%20%E6%95%B0%E7%BB%84%E4%B8%AD%E9%87%8D%E5%A4%8D%E7%9A%84%E6%95%B0%E5%AD%97.md

你给出的这段代码展示了一个非常经典的**原地置换(In-place Swap)**算法。这种技巧在处理“数组元素范围在 $0$$n-1$ 之间”的问题时非常高效。

下面为你详细拆解其时空复杂度的分析逻辑:


1. 时间复杂度:$O(n)$

初看代码,你可能会发现一个 for 循环嵌套了一个 while 循环,直觉上容易误判为 $O(n^2)$。但实际上,它是 均摊 $O(n)$

为什么是 $O(n)$

  • 核心逻辑:每一次 swap 操作,都会把至少一个数字送到它“应该在”的位置(即 nums[i] == i)。

  • 归位原则:一旦一个数字被送到了正确的位置,它在后续的遍历中就不会再被移动。

  • 计数分析:虽然 while 循环可能在某一次执行中循环多次,但对于长度为 $n$ 的数组,最多只需要 $n-1$ 次交换,就能让所有数字各就各位(或者发现重复)。

  • 结论:每个元素最多被访问和交换常数次,因此总的时间复杂度是 $O(n)$


2. 空间复杂度:$O(1)$

这个比较直观,但也是该算法的精妙之处。

  • 原地操作:算法直接在输入的 nums 数组上进行交换和比较。

  • 额外变量:除了循环变量 i 和交换时用到的临时变量 t,没有申请任何与输入规模 $n$ 相关的额外空间(如 HashSet 或临时数组)。

  • 结论:空间复杂度为极优的 $O(1)$


3. 代码小瑕疵纠正

你提供的代码段中有一个小的逻辑冗余和错误,我帮你标注并优化了一下:

Java

public int duplicate(int[] nums) {
    if (nums == null || nums.length <= 1) return -1;

    for (int i = 0; i < nums.length; i++) {
        // while 循环保证将 nums[i] 换到它正确的位置上
        while (nums[i] != i) {
            // 如果要交换的目标位置已经有了正确的数,说明抓到重复的了!
            if (nums[i] == nums[nums[i]]) {
                return nums[i];
            }
            // 交换
            swap(nums, i, nums[i]);
        }
        // 注意:原代码这里多了一个 swap(nums, i, nums[i]),是不需要的
    }
    return -1;
}

关键点拨:

  1. 死循环风险:原代码 while 块后的那个 swap 是多余的,且逻辑上容易让人困惑。

  2. 核心判断if (nums[i] == nums[nums[i]]) 是灵魂。它的意思是:我手里抓着一个 $2$,我想把它放回“老家”(索引为 $2$ 的位置),结果发现“老家”已经住着一个 $2$ 了。嘿,这就是重复!


4. 字节面试避坑指南

如果你在面试中写出这个解法,面试官通常会追问:

  • “如果要求不能修改原数组怎么办?”

    • 这时你就不能用“原地置换”了。

    • 方案 A:使用 HashSet(空间 $O(n)$)。

    • 方案 B:使用二分法的思想(时间 $O(n \log n)$,空间 $O(1)$),通过统计区间内数字出现的次数来锁定重复值。

想试试看如何用“二分法”在不修改数组的情况下找出重复数字吗?我们可以接着聊聊这个思路。

正文完
 0
bdspAdmin
版权声明:本站原创文章,由 bdspAdmin 于2026-03-12发表,共计1337字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)