zoukankan      html  css  js  c++  java
  • 查找数组中重复的数字(二)

    需求

    不修改数组找出重复的数字。
    在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,单不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,6,2,6,7},那么对应的输出是重复的数字2或者6。(不要求找出所有重复数字,只要找出其中之一即可)

    分析

    由于不能修改输入的数组,因此不能在原始数组中做排序再判断下标。需要考虑其他的方法。

    思路一:

    使用一个辅助的数组,长度为n,如:tmp[n],然后开始遍历原始数组
    1.如果数组中出现数字x,就判断数组下标为x的节点是否还是初始值0,即tmp[x]是否为0;
    2.如果tmp[x] == 0,就标记为1,然后继续遍原始数组的下一个元素,继续步骤1;
    3.如果tmp[x] != 0,说明之前已经表标记为1了,即之前已经出现过这个相同的元素,即已经找到重复数字了。
    见示例代码中的findRepeatNumberWithExArray

    思路二:

    暂且称之为分段法。

    假如数组如下{2,3,5,4,6,2,6,7}
    数组的取值范围在1~7,数组的长度为8,所以在1~7里面肯定是有1个或者1个以上重复的数字。
    1.把1~7的取值范围分成两部分,前半部分为1~4,后半部分是5~7;
    2.计算1~4的数字有多少个,从此例子来看,一共有4个(2,3,4,2),1~4之间范围为4,数组的长度为4,有可能有重复的数字也有可能没有重复的数字;
    3.由于剩下的数字范围是5~7范围为3,数组长度是4(总长度8减去前半部分数字的长度,就是后半部分剩余的长度4),因此肯定有重复数字;
    4.查找范围变成5~7,重复步骤1,把取值范围分成两部分,前半部分是5~6,后半部分是7;接着继续步骤二;
    ...
    最后.当查找的数字范围变成1,并且计算得出的数字个数大于1,说明这个数字就是重复的数字,任务结束。
    见实例代码中的findRepeatNumberBySegment

    c++示例代码如下:

      1 #include <iostream>
      2 
      3 using namespace std;
      4 
      5 const int ARR_LENGTH = 8;
      6 
      7 /************************************************************************/
      8 /* @brief 使用额外的辅助数组查找数组中其中一个重复数字
      9 /* @param arr数组
     10 /* @param length数组长度
     11 /* @return 数组中其中一个重复的数字,-1表示没找到
     12 /************************************************************************/
     13 int findRepeatNumberWithExArray(const 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     //一个临时的辅助数组
     24     int *tmpArr = new int[length];
     25     memset(tmpArr, 0, sizeof(int)*length);
     26 
     27     for (int i = 0; i < length; ++i)
     28     {
     29         if (tmpArr[arr[i]] != 0)
     30         {
     31             result = arr[i];
     32             break;
     33         }
     34         else
     35         {
     36             tmpArr[arr[i]] = 1;
     37         }
     38     }
     39     delete[] tmpArr;
     40     tmpArr = nullptr;
     41     return result;
     42 }
     43 
     44 /************************************************************************/
     45 /* @brief 计算数组中在某一个范围内的数字的数量
     46 /* @param arr数组
     47 /* @param length数组长度
     48 /* @param start范围开始位置(包括start)
     49 /* @param end范围结束位置(包括end)
     50 /* @return 在范围内的数字的数量
     51 /************************************************************************/
     52 int countRange(const int* arr, const int length, const int start, const int end)
     53 {
     54     int counter = 0;
     55     if (!arr || length < 1 || end < start)
     56     {
     57         return counter;
     58     }
     59 
     60     for (int i = 0; i < length; ++i)
     61     {
     62         //如果数字在范围内则把个数加1
     63         if (arr[i] >= start && arr[i] <= end)
     64         {
     65             counter++;
     66         }
     67     }
     68     return counter;
     69 }
     70 
     71 /************************************************************************/
     72 /* @brief 使用分段法查找数组中其中一个重复数字
     73 /* @param arr数组
     74 /* @param length数组长度
     75 /* @return 数组中其中一个重复的数字,-1表示没找到
     76 /************************************************************************/
     77 int findRepeatNumberBySegment(const int* arr, const int length)
     78 {
     79     int result = -1;
     80 
     81     //入参有问题直接返回,少于2个数字肯定没有重复的
     82     if (!arr || length < 2)
     83     {
     84         return result;
     85     }
     86 
     87     int start = 1;
     88     int end = length - 1;
     89     int counter = 0;
     90 
     91     while (start <= end)
     92     {
     93         int middle = (end - start) / 2 + start;
     94         //计算前半部分的数字的数量
     95         counter = countRange(arr, length, start, middle);
     96         //只有一个数字并且数量大于1,说明找到了重复的数字了
     97         if (start == middle && counter > 1)
     98         {
     99             result = start;
    100             break;
    101         }
    102         
    103         //假如前半部分数字数量少于等于范围,说明这个范围可能有重复的也有可能没有重复的数字,但是后一半的数字肯定有重复的,直接在后一半查找
    104         if (counter <= (middle - start + 1))
    105         {
    106             start = middle + 1;
    107         }
    108         //假如前半部分数字的数量大于数字范围,说明这个范围内肯定有重复的数字,继续查找
    109         else
    110         {
    111             end = middle;
    112         }
    113     }
    114     return result;
    115 }
    116 
    117 int main()
    118 {
    119     int arr[ARR_LENGTH] = {2,3,5,4,6,2,6,7};
    120 
    121     cout << "原始数据:" << endl;
    122 
    123     for (int i = 0; i < sizeof(arr) / sizeof(int); ++i)
    124     {
    125         cout << arr[i] << "  ";
    126     }
    127 
    128     int repeat = findRepeatNumberWithExArray(arr, ARR_LENGTH);
    129     cout << "
    
    辅助数组法查找重复数字:" << repeat << endl;
    130 
    131     repeat = findRepeatNumberBySegment(arr, ARR_LENGTH);
    132     cout << "
    
    分段法查找重复数字:" << repeat << endl;
    133 
    134     return 0;
    135 }

    运行结果:

  • 相关阅读:
    在酷热的就业天气寻找几丝凉意
    《Orange ’ s :一个操作系统的实现 》作者自序
    Windows的设备驱动框架
    Windows的设备驱动框架中的上层与下层模块
    如何搭建自己的开发环境
    数据库设计指南(二)设计表和字段
    软件敏捷架构师
    Struts到JSF/Tapestry
    敏捷开发
    barcode制作条形码及破解
  • 原文地址:https://www.cnblogs.com/huangwenhao/p/11165611.html
Copyright © 2011-2022 走看看