剑指03-数组中重复的数字

剑指03-数组中重复的数字

一、原题题目

1.1 题目

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

1.2 示例

  • 示例1:

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

提示:

  • 可以假设 st 长度相同。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcofhttps://leetcode-cn.com/problems/isomorphic-strings/

二、解题思路

2.1 利用【暴力遍历】解题

  • 解题思路

  题目的要求是找到重复的数字,那么如果我们用暴力遍历的方法,第一个数字和后面的比较,如果没有重复就 i++ ,然后继续,这种方法需要时间复杂度是 o(n^2),空间复杂度是 o(n) ,显然时间复杂度太高了,那么我们就会想如何才能减少时间复杂度来找到重复的数字呢?

  • 详细代码(Java)
public int findRepeatNumber(int[] nums) {
  for (int i = 0 ;i<nums.length;i++){
    for (int j = i+1;j<nums.length;j++){
      if (nums[i]==nums[j])
        return nums[i];
    }
  }
  return 0;
}
  • 代码执行结果

  执行结果理所当然的超时,我们一般这种降低时间复杂度的方法就是,通过空间换时间,就是例如哈希表或者集合,把遍历过的值记录下来,这样就可以节约大量的时间,所以方法二就是使用哈希表把遍历过的元素存下来,如果发现一次就记录1,发现两次就记录2,并且每次遍历检查,如果大于等于2了,就返回该重复的数字,这种方法很好理解,也是大多数人会使用的方法,这种方法的好处在于把时间复杂度降低到了o(n),但同样付出的代价是空间复杂度上升到了o(n),这是很典型的空间换时间的题目,同样的题目,如两数之和也是类似。

2.2 利用【空间换时间】提升性能

  • 解题思路

  可以利用哈希表,数组,集合等来记录每个数组出现的情况,从而判断数组内的数字是否有重复,最后发现用数组记录的时间复杂度最低。

  • 详细代码(Java):哈希表
public int findRepeatNumber(int[] nums) {
  HashMap<Integer,Integer> map = new HashMap<>();
  for (int i = 0;i <nums.length;i++){
    if (map.containsKey(nums[i]))   return nums[i];
    map.put(nums[i],1);
  }
  return 0;
}
  • 哈希表代码执行结果
利用哈希表执行结果
  • 详细代码(Java):集合
public int findRepeatNumber(int[] nums) {
  Set<Integer> set = new HashSet<Integer>();
  for (int x : nums){
    if (!set.add(x))    return x;
  }
  return 0;
}
  • 代码执行结果
利用集合执行结果
  • 详细代码(Java):数组
public int findRepeatNumber(int[] nums) {
  boolean [] num = new boolean[nums.length];
  for (int i = 0; i < nums.length; i++){
    if (num[nums[i]] == false){
      num[nums[i]] = true;
    }else {
      return nums[i];
    }
  }
  return 0;
}
  • 代码执行结果
利用数组执行结果

2.3 利用【排序】解题

  • 解题思路

  再想提升性能可能就要在空间上下注意了,能不能原数组上实现题目要求呢?应该是有的,第一种方法我想到的是先对数组排序,如果存在两个连续的数字相同不就是重复的了嘛。

  • 详细代码(Java)
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 0;
}
  • 代码执行结果
排序执行结果

2.3 【原地置换】解题

  • 解题思路

  因为题目告诉我们的信息中是数字只会小于数组的个数,那么也就是说,如果没有重复的,那么每个数组应该都可以对应到一个下标位置。具体可以理解为一个萝卜一个坑。用下面的例子来看更直接。

nums[i]     3  1  0  2   萝卜   
     i       0  1  2  3   坑  

  0号坑说我要的是0号萝卜,不要3号萝卜,所以会去和3号坑的萝卜交换,因为如果0号坑拿了3号坑的3号萝卜,那说明3号坑装的也肯定是别人家的萝卜,所以要跟3号坑换,换完是这样的:

 nums[i]     2  1  0  3   萝卜  
     i       0  1  2  3   坑 

  然而0号坑还没找到自己的萝卜,它不要2号萝卜,又去和2号坑的萝卜交换,换完是这样的:

 nums[i]     0  1  2  3   萝卜 
     i       0  1  2  3   坑  

  这时候刚好就是一一对应的,交换过程也不会出现不同坑有相同编号的萝卜。要注意交换用的是while,也就是0号坑只有拿到0号萝卜,1号坑才能开始找自己的萝卜。

  如果有重复元素,例如:

 nums[i]     1  2  3  2    萝卜
     i       0  1  2  3    坑

  同样的,0号坑不要1号,先和1号坑交换,交换完这样的:

 nums[i]     2  1  3  2    萝卜
     i       0  1  2  3    坑

  0号坑不要2号萝卜,去和2号坑交换,交换完这样的:

 nums[i]     3  1  2  2    萝卜
     i       0  1  2  3    坑

  0号坑不要3号萝卜,去和3号坑交换,交换完这样的:

 nums[i]     2  1  2  3    萝卜
     i       0  1  2  3    坑

  0号坑不要2号萝卜,去和2号坑交换,结果发现你2号坑也是2号萝卜,那我还换个锤子,同时也说明有重复元素出现。

  • 详细代码(Java)
public int findRepeatNumber(int[] nums) {
  int temp;   // 定义临时交换变量
  for (int i = 0;i<nums.length;i++){  // 从第0个坑位开始找对应的萝卜
    while (nums[i]!=i){             // 当前坑位不是自己对应的萝卜 而是nums[i]坑位的
      // 如果发现nums[i]坑位的萝卜是它自己坑位的,说明自己的是重复多的
      if (nums[i]==nums[nums[i]]) 
        return nums[i];
      // 如果发现nums[i]坑位的萝卜不是它自己坑位的,先交换过来再继续去找
      temp = nums[i];
      nums[i]=nums[temp];
      nums[temp] = temp;
    }
  }
  return 0;
}
  • 代码执行结果
原地置换执行结果
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ItDaChuang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值