本文最后更新于:4 个月前

题目描述

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。

示例 1:

输入: [1,3,4,2,2]
输出: 2

示例 2:

输入: [3,1,3,4,2]
输出: 3

说明:

不能更改原数组(假设数组是只读的)。
只能使用额外的 O(1) 的空间。
时间复杂度小于 O(n^2) 。
数组中只有一个重复的数字,但它可能不止重复出现一次。

题解

本题目限定了元素的取值范围”其数字都在 1 到 n 之间(包括 1 和 n)”。

另外,“说明”里的也特别重要~

说明:
> 不能更改原数组(假设数组是只读的)。
> 只能使用额外的 O(1) 的空间。
> 时间复杂度小于 O(n^2) 。
> 数组中只有一个重复的数字,但它可能不止重复出现一次。

最后解决的思路很简单,就是使用双指针,直接看代码注释即可。

代码

class Solution {
    public int findDuplicate(int[] nums) {
        // 如果数组就只有两个元素,直接返回即可
        if(nums.length <= 2) { return nums[0]; }
        
        int q = 0, s = 0;
        int indexStart = 0; // indexStart用以记录起步位置
        
        // 初始化q、s应该指向[第一个index!=nums[index]的元素],不应该为0
        for(int index = 0; index < nums.length; index++) {
            if(index != nums[index]) {
                q = index;
                s = index;
                indexStart = index; // 保存起步位置
                break;
            }
        }

        // 快慢指针从同一位置(index)起步
        while(true) {
            // 快指针q的步长=2
            q = nums[nums[q]];
            // 慢指针s的步长=1
            s = nums[s];
            // 两指针相遇,结束第一次循环
            if(q == s) {
                // 慢指针回到初始起步点indexStart
                s = indexStart;
                break;
            }
        }

        // 快慢指针继续一起移动,但要修改快指针的步长
        // 快慢指针再相遇的元素,即所要的结果
        while(true) {
            // 快指针q的步长=1
            q = nums[q];
            // 慢指针s的步长=1
            s = nums[s];
            // 两指针相遇,结束循环
            if(q == s) { break; }
        }

        return s;
    }
}

提交

题后

今天这“每日一题”和《【LeetCode】面试题03. 数组中重复的数字》有些类似。
不同的是,今天这题限定了一些特别的条件,比如:

  • 不能更改原数组(假设数组是只读的)
  • 只能使用额外的 O(1) 的空间
  • *时间复杂度小于 O(n^2) *
    尤其是对复杂对的要求,这就提高了算法设计的难度。


我想着各种优化方法,很艰难地,最后找到了这种。
哇地一声就哭出来了,太难了/(ㄒoㄒ)/~~


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

免费域名注册 上一篇
通过css样式实现动态背景 下一篇

 目录