面试题3:数组中重复的数字

一.题目一

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

例如:
  输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3

二.思路

  1. 运用哈希表不重复的特点,扫描数组,扫描得到的元素不在哈希表中,则把该元素放入哈希表,继续扫描;若扫描到的元素在哈希表中,则该元素为重复元素,返回该元素;
  2. 上述方法很简单,但是空间复杂度为O(n),考虑是不是能把空间复杂度降低一下?我们发现数组里面的所有数字都在0~n-1 的范围内,所以有如下想法:把数组重排的话,如果不重复,那么位置为i的元素大小应该也为i,如果有重复元素,位置为i的元素应该至少有两个元素,也就是出现了碰撞。因此我们决定重排数组,把元素放到对应的位置,一旦出现碰撞,则出现重复。

三.代码

方法一代码:

//unordered_map
bool duplicate(int numbers[], int length, int* duplication){
  
    unordered_map<int,int> umap;

    if(numbers == nullptr || length <= 0)
      return false;

    for(int i = 0;i < length;i++)
      if(numbers[i] < 0 || numbers[i] > length - 1)
        return false;

    for(int i = 0;i < length;i++){
      if(!umap.count(numbers[i])){
        *duplication = numbers[i];
        return true;
      }
        umap[numbers[i]] = i;

    }  
    return false;
}

方法二代码:

//collision
bool duplicate(int numbers[], int length, int* duplication){
    
    if(numbers == nullptr || length <= 0)
        return false;

    for(int i = 0;i < length;i++)
        if(numbers[i] < 0 || numbers[i] > length - 1)
            return false;

    for(int i = 0;i < length;i++){

        while(numbers[i] != i){
            if(numbers[i] == numbers[numbers[i]]){
                *duplication = numbers[i];
                return true;
            }

            int tmp = numbers[i];
            numbers[i] = numbers[tmp];
            numbers[tmp] = tmp;

        }


    }
    return false;

}

四.本题考点

  • 考查应聘者对一维数组的理解及编程能力。一维数组在内存中占据连续的空间,因此我们可以根据下标定位对应元素。
  • 考查应聘者分析问题的能力。当应聘者发现问题比较复杂时,能不能通过具体的例子找出其中的规律,是能否解决这个问题的关键所在。

五.题目二

       在一个长度为 n+1 的数组里的所有数字都在 1~n 的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组

例如,

       如果输入长度为8的数组{2, 3, 5, 4, 3, 2, 6, 7},那么对应的输出是重复的数字2或者3。

六.思路

  1. 考虑一个辅助数组,同样采用题目一的方法二,只不过这次是在辅助数组上做操作
  2. 上述方法空间复杂度为O(n),我们同样考虑如何降低空间复杂度。可以考虑二分查找的思路:我们把1~n的数字从中间的数字m分为两部分,前面一半为1~m,后面一半为m+1~n。如果前面1~m部分的数字的数目超过m,那么这一部分一定包含重复的数字;否则,另一半m+1~n的部分必定包含重复数字。我们可以把包含重复数字的部分继续一分为二,直到找到一个重复的数字。在这个过程中,我们要加一个子函数,计算某一部分里数字的部分。

七.代码

方法一代码

int getDuplication(const int* numbsers,int length){
  
  if(numbers == nullptr || length <= 0)
    return false;

  int tmp[length];

  for(int i = 0;i < length; i++){
    if(numbers[i] < 1 || numbers[i] > length)
      return 0;

    if(tmp[i])
      return 1;

    tmp[i] = numbers[i];
  }

  return 0;

}

方法二代码

int getDuplication(const int* numbers,int length){
    if(numbers == nullptr || length < 0)
        return 0;

    int start = 0;
    int end = length - 1;

    while(start <= length){
        int mid = start + ((end - start) >> 1);

        int count = countRange(numbers,length,start,mid);
        if(start == end){
            if(count > 1)
                return start;
            else
                return -1;
        }

        if(count > (mid - start + 1))
            end = mid - 1;

        else
            start = mid + 1;

    }

    return -1;

}

int countRange(int* numbers,int length,int start,int end){
    
    if(numbers == nullptr)
        return 0;

    int count = 0;
    for(int i = start;i < end;i++){
        if(numbers[i] >= start && numbers[i] <= end)
            ++count;
    }

    return count;
}

八.本题考点

  • 考查应聘者对一维数组的理解及编程能力。一维数组在内存中占据连续的空间,因此我们可以根据下标定位对应的元素。
  • 考查应聘者对二分查找算法的理解,并能快速、正确的实现二分查找算法的代码
  • 考查应聘者的沟通能力。应聘者只有具备良好的沟通能力,才能充分了解面试官的需求,从而有针对性的选择算法解决问题。
原文地址:https://www.cnblogs.com/ovs98/p/9852939.html