剑指 offer 刷题——03. 数组中重复的数字

一、题目

找出数组中重复的数字。

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3 

限制:

2 <= n <= 100000

二、思路及代码实现

思路一:排序

解决这个问题的一个简单的方法是先对数组进行排序,使得重复数字位于相邻的位置,再遍历数组,比较每个数字和它下一个位置的数字是否相等,若相等则将该数字返回。

排序一个长度为 n n n 的数组需要 O ( n l o g n ) O(nlogn) O(nlogn) 的时间,再遍历数组需要 O ( n ) O(n) O(n) 的时间。

参考代码:

class Solution {
    public int findRepeatNumber(int[] nums) {
        Arrays.sort(nums);
        for(int i = 0; i < nums.length - 1; i++){
            if(nums[i] == nums[i + 1])
                return nums[i];
        }
        return -1;
    }
}

思路二:使用集合

由于只需要找到任意一个重复的数字,所以我们遍历数组,遇到重复的数字返回即可。

我们可以使用集合类来实现(Set 和 Map 都可以):遍历数组把数组元素存储在集合里,如果当前数字已经在集合里,则当前数字是重复数字。这个算法的时间复杂度是 O ( n ) O(n) O(n)

参考代码:

class Solution {
    public int findRepeatNumber(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for(int i = 0; i < nums.length; i++){
            if(!set.add(nums[i]))
                return nums[i];
        }
        return -1;
    }
}

思路三:(用时最短)

剑指 offer 面试官精讲:

我们注意到数组中所有数字都在 0 ~ n − 1 0~n-1 0n1 的范围内。如果没有重复数字,那么当数组排序后数字 i i i 将出现在下标为 i i i 的位置。由于数组中存在重复数字,那么有些位置可能存在多个数字,同时有些位置可能没有数字。

现在我们重排这个数组。从头到尾依次扫描这个数组的每个数字。当扫描到下标为 i i i 的数字时,首先比较这个数字(设为 m m m)是不是等于 i i i。如果是,则接着扫描下一个数字;如果不是,则再拿它和第 m m m 个数字进行比较。如果它和第 m m m 个数字相等,就找到了一个重复数字(因为该数字在下标为 i i i m m m 的位置都出现了);如果它和第 m m m 个数字不相等,就把第 i i i 个数字和第 m m m 个数字交换,把 m m m 放在属于它的位置。重复这个过程,直到我们发现一个重复数字。

以数组 {2,3,1,0,2,5,3} 为例分析寻找重复数字的过程:

  • 数组的第 0 个数字是 2,与它的下标不相等,于是把它和下标为 2 的数字交换,得到 {1,3,2,0,2,5,3};
  • 此时,第 0 个数字是 1,仍然与它的下标不相等,继续把它和下标为 1 的数字交换,得到 {3,1,2,0,2,5,3};
  • 这时第 0 个数字仍然和它的下标不相等,继续交换第 0 个数字和第 3 个数字,得到{0,1,2,3,2,5,3};
  • 此时第 0 个数字为 0,扫描下一个数字。后面的第 1、2、3 个数字和它们的下标相等,不需要执行任何操作;
  • 第 4 个数字是 2,和它的下标不相等,于是比较它和下标为 2 的数字。而此时下标为 2 的数字也是 2,也就是说这两个位置的数字重复。

参考代码:

class Solution {
    public int findRepeatNumber(int[] nums) {
        for(int i = 0; i < nums.length; i++){
            while(nums[i] != i){
                if(nums[i] == nums[nums[i]])
                    return nums[i];
                // 交换
                int t = nums[i];
                nums[i] = nums[t];
                nums[t] = t;
            }
        }
        return -1;
    }
}

代码尽管有一个两重循环,但每个数字最多只要交换两次节能找到自己的位置,因此总的时间复杂度 O ( n ) O(n) O(n);所有的操作都在输入数组上进行,不需要额外分配内存,空间复杂度为 O ( 1 ) O(1) O(1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值