• 查找数组中重复的数字(一)


    需求

      找出数组中重复的数字。

      在一个长度为n的数组里的所有数字都在0~n-1的范围内,数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。(不要求输出所有的重复数字,找到其中之一即可)

    分析

    思路一:

    对输入的数组先进行排序,然后从头到位遍历排序好的数组,当出现第一个数组下标与存储的值不一样的(array[i] != i),就说明找到了重复数字。排序需要的时间复杂度为O(nlgn)。见示例代码中的findRepeatNumberWithSort

    思路二:

    使用一个辅助的数组,长度为n,如:tmp[n],然后开始遍历原始数组

    1.如果数组中出现数字x,就判断数组下标为x的节点是否还是初始值0,即tmp[x]是否为0;

    2.如果tmp[x] == 0,就标记为1,然后继续遍原始数组的下一个元素,继续步骤1;

    3.如果tmp[x] != 0,说明之前已经表标记为1了,即之前已经出现过这个相同的元素,即已经找到重复数字了。

    这种方法由于需要使用一个长度为n的辅助数组,空间复杂度为O(n)。见示例代码中的findRepeatNumberWithExArray。

    思路三:

    不使用辅助数组,也不用提前排序的方法,暂且称为动态排序法吧。

    先考虑另外一种情况:

    假如一个排序好的长度为n的数组,数组中的数字为0~n-1,并且没有重复数字,那这个数组中存储的数字应该是:array[0]=0,array[1]=1,array[2]=2....array[n-1]=n-1。也就是说数组的下标应该是跟存储的值是一样的。

    回到这个需求:

    如果数组中的数字有重复,说明肯定存在array[i]=i并且array[j]=i(可能还有array[k]=i,array[l]=i.....)。

    大前提了解了,下面开始按照这个思路查找重复数字。

    从原始数组第0个位置开始遍历,

    1.假如 array[i] == i 说明,这个位置放置了对的数值,然后开始查检查array[i+1];

    2.假如 array[i] != i ,假如值为x,判断array[x]是不是等于x,如果是说明array[i]和array[x]都等于x,查找到了2个x,说明任务结束,可以返回结果了;

    3.假如 array[i] != i ,假如值为x,判断array[x]是不是等于x,如果不是,就把array[i]跟array[x]的数字交换,使得array[x]=x,这样x就放到了array[x]的位置上,也就是找到对应的位置了,然后继续步骤1,判断当前的array[i]的数字。

    通过这种思路,可以看到不需要提前对数组进行排序,只需要在判断的时候顺便排序即可,只需一个循环,时间复杂度是O(n),由于只需要使用一个辅助的空间用于交换数据,空间复杂度是O(1)【ps:甚至可以不用辅助空间来交换两个数字】。

    见示例代码中的findRepeatNumberDynamicSort。

    c++示例代码如下:

      1 #include <iostream>
      2 #include <algorithm>
      3 
      4 using namespace std;
      5 const int ARR_LENGTH = 7;
      6 
      7 /************************************************************************/
      8 /* @brief 使用sort查找数组中其中一个重复数字
      9 /* @param arr数组
     10 /* @param length数组长度
     11 /* @return 数组中其中一个重复的数字,-1表示没找到
     12 /************************************************************************/
     13 int findRepeatNumberWithSort(int* arr, const int length)
     14 {
     15     int result = -1;
     16 
     17     //入参有问题直接返回,少于2个数字肯定没有重复的
     18     if (!arr || length < 2)
     19     {
     20         return result;
     21     }
     22 
     23     sort(arr, arr + length);
     24     for (int i = 0; i < length; ++i)
     25     {
     26         if (arr[i] != i)
     27         {
     28             result = arr[i];
     29             break;
     30         }
     31     }
     32     return result;
     33 }
     34 
     35 /************************************************************************/
     36 /* @brief 使用额外的辅助数组查找数组中其中一个重复数字
     37 /* @param arr数组
     38 /* @param length数组长度
     39 /* @return 数组中其中一个重复的数字,-1表示没找到
     40 /************************************************************************/
     41 int findRepeatNumberWithExArray(int* arr, const int length)
     42 {
     43     int result = -1;
     44 
     45     //入参有问题直接返回,少于2个数字肯定没有重复的
     46     if (!arr || length < 2)
     47     {
     48         return result;
     49     }
     50 
     51     //一个临时的辅助数组
     52     int *tmpArr = new int[length];
     53     memset(tmpArr, 0, sizeof(int)*length);
     54 
     55     for (int i = 0; i < length; ++i)
     56     {
     57         if (tmpArr[arr[i]] != 0)
     58         {
     59             result = arr[i];
     60             break;
     61         }
     62         else
     63         {
     64             tmpArr[arr[i]] = 1;
     65         }
     66     }
     67     delete[] tmpArr;
     68     tmpArr = nullptr;
     69     return result;
     70 }
     71 
     72 /************************************************************************/
     73 /* @brief 不使用辅助数组,动态排序法查找重复数字
     74 /* @param arr数组
     75 /* @param length数组长度
     76 /* @return 数组中其中一个重复的数字,-1表示没找到
     77 /************************************************************************/
     78 int findRepeatNumberDynamicSort(int* arr, const int length)
     79 {
     80     int result = -1;
     81 
     82     //入参有问题直接返回,少于2个数字肯定没有重复的
     83     if (!arr || length < 2)
     84     {
     85         return result;
     86     }
     87 
     88     int i = 0;
     89     while (i < length)
     90     {
     91         int key = arr[i];
     92         //如果数组下标和存储的值相同(arr[i]等于i),说明这个值已经放到了它应该在的地方(即已经排序到了正确的位置),继续数组中的下一个值
     93         if (key == i)
     94         {
     95             ++i;
     96         }
     97         else
     98         {
     99             //由于key跟i不相同,并且arr[key],arr[i]这两个位置都存储了同一个值key,说明找到了相同的数字,结束循环,返回结果
    100             if (arr[key] == key)
    101             {
    102                 result = key;
    103                 break;
    104             }
    105             //让arr[i]和arr[key]的值交换,使得arr[key]这个位置存放他该放的值,即arr[key]等于key
    106             else
    107             {
    108                 arr[i] = arr[key];
    109                 arr[key] = key;
    110             }
    111         }
    112     }
    113     return result;
    114 }
    115 
    116 int main()
    117 {
    118     int arr[ARR_LENGTH] = { 2,3,1,0,2,5,3 };
    119     cout << "原始数据:" << endl;
    120 
    121     for (int i = 0; i < sizeof(arr) / sizeof(int); ++i)
    122     {
    123         cout << arr[i] << "  ";
    124     }
    125 
    126     int repeat = findRepeatNumberWithSort(arr, ARR_LENGTH);
    127     cout << "
    
    排序法查找重复数字:" << repeat << endl;
    128 
    129     repeat = findRepeatNumberWithExArray(arr, ARR_LENGTH);
    130     cout << "
    辅助数组法查找重复数字:" << repeat << endl;
    131 
    132     repeat = findRepeatNumberDynamicSort(arr, ARR_LENGTH);
    133     cout << "
    动态排序法查找重复数字:" << repeat << endl;
    134 
    135     return 0;
    136 }

    运行结果:

  • 相关阅读:
    【洛谷 4613】Olivander
    【洛谷 1385】密令
    【洛谷 4439】Aron
    【洛谷 3383】线性筛素数
    【洛谷 2412】查单词
    【洛谷 1980】计数问题
    【洛谷 3372】(模板)线段树 1
    Luogu P3743 kotori的设备
    Luogu P2340 [USACO03FALL]Cow Exhibition G
    Luogu P3047 [USACO12FEB]Nearby Cows G
  • 原文地址:https://www.cnblogs.com/huangwenhao/p/11163002.html
Copyright © 2020-2023  润新知