【数学】C024_错误集合(暴力枚举+优化 | 排序 | map计数 | 数组计数)

一、题目描述

The set S originally contains numbers from 1 to n.
But unfortunately, due to the data error,
one of the numbers in the set got duplicated to another number in the set,
which results in repetition of one number and loss of another number.

Given an array nums representing the data status of this set after the error.
Your task is to firstly find the number occurs twice and then find the number that is missing.
Return them in the form of an array.

Example 1:
Input: nums = [1,2,2,4]
Output: [2,3]
 
Note:
The given array size will in the range [2, 10000].
The given array's numbers won't have any order.

二、题解

(1) 暴力枚举

暴力枚举,对每一个数字进行检查是否重复,重复则计数器count++。每次检查完一个数字,立刻检查该数字出现的次数。

  • count==2,则为重复数字
  • count==0,则为缺失数字

如果想通用一点,把循环 for 的 i=1 改为i=nums[i],然后在外面初始化一个int i=0;即可。

/**
 * 执行用时 445ms 击败了 5.12% 的java用户
 * 内存消耗 40MB 击败了 92% 的java用户
 * @param nums
 * @return
 */
public int[] findErrorNums1(int[] nums) {
  int dup = 0;
  int miss = 0;

  for (int i = 1; i <= nums.length; i++) {
    int count=0;
    for (int j = 0; j < nums.length; j++) {
      if(nums[j] == i)
        count++;
    }
    // 对每一个数字检查后,如果是i是重复数字/缺失那么赋值
    if(count == 2)  dup=i;
    else if(count == 0)  miss=i;
  }
  return new int[]{dup, miss};
}

(2) 优化暴力

/**
 * 优化暴力:找到dup和miss之后,就退出循环
 * 执行用时 306 ms 击败了 5.12% 的java用户
 * 内存消耗 39MB 击败了 95% 的java用户
 * @param nums
 * @return
 */
public int[] findErrorNums2(int[] nums) {
  int dup = 0;
  int miss = 0;

  for (int i = 1; i <= nums.length; i++) {
    int count=0;
    for (int j = 0; j < nums.length; j++) {
      if(nums[j] == i)
        count++;
    }
    // 对每一个数字检查后,如果是i是重复数字/缺失那么赋值
    if(count == 2)  dup=i;
    else if(count == 0)  miss=i;
    if(dup > 0 && miss > 0) return new int[]{dup, miss};
  }
  return new int[]{dup, miss};
}

(3) 排序

不难发现,如果对数组进行排序,那么重复的两个数字会出现在相邻位置。根据这一点我们可以得到的逻辑是:

  • nums[i-1] == nums[i],则 nums[i-1] 就是重复数字
  • nums[i] >= nums[i-1] + 2,则 nums[i-1] + 1 就是缺失数字
/**
 * 执行用时  15ms 击败了 33% 的java用户
 * 内存消耗 40MB 击败了 95% 的java用户
 * @param nums
 * @return
 */
public int[] findErrorNums3(int[] nums) {
  int dup = 0;
  int miss = 1;
  Arrays.sort(nums);
  for (int i = 1; i < nums.length; i++) {
    if(nums[i-1] == nums[i])
      dup=nums[i];
    else if(nums[i] >= nums[i-1] + 2) // 比如1224中的4-2>=2
      miss=nums[i]-1;
  }
  // 注意这里的判断,如果nums=={1,1}怎么办? 如果nums=={2,2}怎么办
  return new int[]{dup, nums[nums.length-1] != nums.length ? nums.length : miss};
}

(4) HasMap 计数法

遍历两次

  • 第一次遍历用map记录元素出现的次数
  • 第二次遍历的区间是从[1, nums.length]
    • 对于重复数字,map 得到的 value 就是 2
    • 对于缺失数字,那么 map 则不会插入该数字
/**
 * 执行用时 62 ms 击败了 5.12 % 的java用户
 * 内存消耗 40 MB 击败了 94 % 的java用户
 * @param nums
 * @return
 */
public int[] findErrorNums4(int[] nums) {
  HashMap<Integer, Integer> map = new HashMap<>();
  int dup = 0;
  int miss = 0;
  for(int n : nums) {
    map.put(n, map.getOrDefault(n, 0) + 1);
  }
  for (int i=1; i <= nums.length; i++) {
    if(map.containsKey(i) && map.get(i) == 2)
      dup = i;
    else if(!map.containsKey(i))
      miss = i;
  }
  return new int[]{dup, miss};
}

(5) 数组计数法

使用 map,每个数字都需要两个变量分别存储数字本身和出现次数,因此存储 n 个数字和出现次数需要 2n 的空间。

如果使用数组存储下标存储数字,数组的值存储出现的次数。,可以将空间减少到 n。

/**
 * 执行用时 3 ms 击败了 74.5% 的java用户
 * 内存消耗 39.1 MB 击败了 95.14 % 的java用户
 * @param nums
 * @return
 */
public int[] findErrorNums5(int[] nums) {
  int dup = 0;
  int miss = 0;
  int[] record = new int[nums.length+1];
  for (int i = 0; i < nums.length; i++) {
    record[nums[i]] += 1;
  }
  for(int i = 1; i <= nums.length; i++) {
    if(record[i] == 2)
      dup = i;
    else if(record[i] == 0)
      miss = i;
  }
  return new int[] {dup, miss};
}
发布了300 篇原创文章 · 获赞 48 · 访问量 8068

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/103995968
今日推荐