《剑指offer》-数组中重复的数字

本文介绍了三种高效查找数组中重复数字的方法:排序遍历、哈希表和类似桶排序的算法,详细解释了每种方法的实现过程、时间与空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数组中重复的数字

在一个长度为n的数组里的所有数字都在 0~ n-1 的范围内。数组中某些数字是重复的,但是不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如:如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出应该是重复的数字2或者3
第一种方法:排序、遍历

先把输入的数组排序,然后从头到尾扫描排序后的数组就可以了。
时间复杂度:就是排序的时间复杂度,如果是用快排的是,则为 O(n*logn)
空间复杂度:

int[] nums = {2, 3, 5, 0, 2, 5, 3}; // 输入数组
Arrays.sort(nums); // 排序
for(int i=1;i<7;i++){    // 排序之后,从1开始,判断这个数字是否重复,并输出
    if(nums[i] == nums[i-1]){
        System.out.println(nums[i]);

测试用例:

  1. 长度为n的数组里包含1个或多个重复的数字
    1. 包含1个或多个重复的数字,如果只重复1次的话,这样可以。但是某个数字重复多次的话,就会多次重复输出该数字。可以通过立一个 flag,
      1. 前后两个数字相等,判断 flag。
        1. flag == 1,则输出这个相等数字,并将 flag 设为 0
        2. flag == 0,则不输出
      2. 前后两个数字不相等,设 flag = 1;
    2. 如果只需要输出任意一个重复的数字的话,加一个 break / return
  2. 数组中不包含重复的数字,就没有输出呗
  3. 无效的输入测试用例
    1. 空数组:这里需要进行一次判断,如果是空数组或者只有一个数字的数组,则直接返回没有
    2. 长度为n的数组中包含 大于 n-1 的数字:可以输出重复的数字
第二种方法:哈希表

创建一个哈希表,尝试把每一个数字加入到hashset中,如果加入失败,则认为找到了重复的数字
时间复杂度:O(1)的时间判断hashset中是否包含了该数字,O(n)的时间遍历数组
空间复杂度:创建了一个大小为 O(n)的哈希表

int[] nums = {2, 3, 5, 5, 2, 5, 3, 7};
HashSet hs = new HashSet(7);
for(int i=0;i<8;i++){
    if(!hs.add(nums[i])){
        System.out.println(nums[i]);
    }
}

测试用例:三种测试用例的处理方式同上

第三种:类似于桶排序:

如果这 n 个数字中,没有重复的数字时,排序后,每个数字都应该在自己对应的下标。如果有重复的数字,那么有些位置可能存在许多数字,有些位置可能没有数字(类似于桶排序)。

另一种理解,每个数字都有三种状态

  1. 第一次出现,在正确的位置
  2. 不是第一次出现,所以肯定不在正确的位置
  3. 第一次出现,但是不在正确的位置
步骤
  1. 当扫描到下标为 i 的数字时,记录这个数字 numbers[ i ]为 m,判断 m 是否等于
    1. 如果相等的话,接着扫描下一个数字。这里说明数字 m 所在的位置正确,并且是第一次出现。如果是第二次出现,那么m肯定不等于 i,因为 i 的坑已经被第一次出现的数字占了
    2. 如果不相等,则判断 m 是否和 numbers[ m ]相等,也就是判断这个数字之前是否出现过
      1. 如果相等,则找到了一个重复的数字。因为是第二次出现,它的坑被占了
      2. 如果不相等,则将 numbers[ i ] (也就是m) 和 numbers[ m ] 交换位置。也就是说,这个数字是第一次出现,但是不在正确的位置。通过交换,将m换到正确的位置。继续对交换来的数字进行判断
int[] numbers = {2, 3, 1, 3, 2, 5, 3};
int m; // 记录 number[i] 这个数字
int swap;
for(int i=0;i<7;i++){
    m = numbers[i];
    // 这个循环有两个出口
    while(m != i) {  // 这个是第一个出口,如果相等,代表m是第一次出现,则对下一个数字进行扫描
        if(m == numbers[m]){
            System.out.println(m);  // 这是第二个出口,如果相等,则代表m不是第一次出现,则要输出并退出循环
            break;
        } else {
            swap = numbers[i];
            numbers[i] = numbers[m];
            numbers[m] = swap;
        }
        m = numbers[i];
    }
}

测试用例:
这个需要对输入用例的范围做判断。因为超出范围的话,即使是重复出现,也没有对应的下标。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值