zoukankan      html  css  js  c++  java
  • 找出数组中是否有重复的数

    题目是这样的, 数组是无序的, 可能没有重复的数,但最多只可能有一个重复的数,要求用最快的方法找到是否有重复的数。

    乍一想,挺难的,但是其实非常的简单。

    解决办法:

    数组a[N]1N-1N-1个数存放在a[N]中,其中某个数重复一次。写一个函数,找出被重复的数字。时间复杂度必须为oN函数原型:

    int do_dup(int a[],int N)

     

    ××××××××××××××××××××××××××××××××××

    假金条的数学思想

     

    此算法题借鉴了假金条的思想,不过比那个过程简单,存放1N-1,就相当于依次从各个袋中取出1N-1个金条,但是有一个是重的,计算这N个数的和将相当于称总重量,减去1N-1的和(用数学方法求)就可求出来重复的数。总之要充分利用数学知识

     

    const int N = 5;

    int a[N]={2,1,3,1,4};

    int tmp1 = 0;

    int tmp2 = 0;

    for (int i=0; i<N; ++i)

    {

    tmp1+=(i+1);

    tmp2+=a[i];

    }

    printf("重复的数:%d\n",N-(tmp1-tmp2));

     

    上述方法求1~N的和,减去数组总和,即为N-x 的差值,x为待找的数

    可以优化的是1-N的和不用程序算,数学方法直接算了

    可定义一个宏,

    #define sum(x)  (x(x+1)/2)

    当然乘法操作是比较复杂的,当N较小时加几个数的效率可能更高

     

    ××××××××××××××××××××××××××××××××××

    标志数组法

     

    申请一个长度为n-1且均为'0'组成的字符串。然后从头遍历a[n]数组,取每个数组元素a[i]的值,将其对应的字符串中的相应位置置1,如果已经置过1的话,那么该数就是重复的数。就是用位图来实现的。

     

    其实,只要数还是0 -- n-1里面的数,那么可以用这样的方法判断所有的重复数的位置和值。

    比如这样的一个数组

    {2,3,1,2}

    我们生成一个字符串"000";

    然后开始遍历,a[0] = 2;

    所以,字符串的第二位为"0",那么我们将其置为"1"

    字符串为"010"

    重复,字符串为"011",,,,,"111"

    然后,判断a[3] = 2 那么 第二位为"1"

    所以,a[3]为重复数,并且位置为第4位。

     

    上述map等各方法的思路是一致的,即访问过后做标记,再次访问时即可知道是否重复。如果考虑空间复杂度的话,其空间oN

    int do_dup(int arr[],int NUM)

    {

            int *arrayflag = malloc(NUM*sizeof(int));

     

            while(i++<NUM)

            arrayflag[i] = false;

     

            for(int i=0; i<NUM; i++)

            {

                    if(arrayflag[arr[i]] >= false)

                           arrayflag[arr[i]] >= true;           // 置出现标志

                    else

                           return  arr[i]; //返回已经出现的值

            }

    }

     

    ××××××××××××××××××××××××××××××××××

    固定偏移标志法

     

    利用标记法单纯写个O(N)的方法并不难,关键是如何保证两点:

    不改变A[]的初始值

    函数内不开辟另外的O(N)内存空间.

     

    很明显上述方法申请了O(N)内存空间,当N过大时,性能降低了

    因此应利用a[N]本身中值和下标的关系来做标记,处理完成后再清除标记即可

     

    a[N],里面是1N-1。原数组a[i]最大是N-1,若a[i]=K在某处出现后,将a[K]加一次N,做标记,当某处a[i]=K再次成立时,查看a[K]即可知道K已经出现过。a[i]在程序中最大也只是N-1+N=2N-1溢出了怎么办啊???),此为一个限制条件

     

    int do_dup(int arr[],int NUM)

    {

    int temp=0;

     

    for(int i=0; i<NUM; i++)

    {

      if(arr[i]>=NUM)

        temp=arr[i]-NUM;            // 该值重复了,因为曾经加过一次了

      else

        temp=arr[i];

     

      if(arr[temp]<NUM)

      {

        arr[temp]+=NUM; //做上标记

      }

      else

      {

        printf("有重复");   

        return temp;

      }

    }

     

    printf("无重复");

    return -1;

    }

    上面就是时间复杂度O(N), 空间复杂度O(1)的算法!

     

    ××××××××××××××××××××××××××××××××××

    符号标志法

     

    上述方法将元素加一个固定的NUM来做标记,可能会造成溢出。下列方法中利用符号位来做标记,因为1~N都为正值,所以就无限制了。基本思想是用int数组的符号位作哈希表。(反正不是unsigned int符号位闲着也是闲着)

     

    bool dup(int array[],int n)

    {

         for(int i=0;i<n;i++)

         {

             if(array[i]>0) //可以以此作下标去判断其他值

                     {

                   if(array[array[i]]<0)

                              {

                                      return array[i];//已经被标上负值了,有重复

                              }

                  else

                             {

                                     array[array[i]]= array[array[i]]; //否则标记为负

                             }

                     }

             else // |array[i]|代表的值已经出现过一次了

                     {

                   if(array[-array[i]]<0)

                              {

                                      return -array[i];//有重复

                              }

                  else

                             {

                                     array[-array[i]]=array[-array[i]];

                             }

                     }

         }

          return -1;//没有重复

    }

     

    //用于修复数组

    void restorearray(int array[],int n)

    {

            for(int i=0;i<n;i++)

            {

            if(array[i]<0)array[i]= array[i];

            }

    }

    时间复杂度on 空间复杂度o1

    不过时间复杂度on 空间复杂度o1)不只一个重复利用此法也是可以实现的

     

     

    //附上我修改后的算法
    bool do_dup_mal(int arr[], int n, int *pre, int *back)
    {
    int MAX = -1;
    int i = 0;
    int sameVal = -1;
    assert(pre && back);
    *pre = *back = -1;

    for (int j=0; j<n; j++)
    {
    if (arr[j] > MAX) MAX = arr[j];
    }

    char *arrayflag = new char[MAX+1] ;
    if (NULL == arrayflag)
    return -1;
    //while(i++ < MAX) arrayflag[i] = '\0';
    memset(arrayflag, 0, MAX+1 ); // '\0' == 0
    for(i=0; i<n; i++)
    {
    if(arrayflag[arr[i]] == '\0')
    arrayflag[arr[i]] = '\1'; // 置出现标志
    else
    {
    sameVal = arr[i]; //返回已经出现的值
    *back = i;
    break;
    }
    }
    delete[] arrayflag;
    if (i < n)
    {
    for (int j=0; j<n; j++)
    {
    if (sameVal == arr[j])
    {
    *pre = j;
    return true;
    }
    }
    }
    return false;
    }





    int main(int argc, char *argv[])
    {
    int prePos = -1, backPos = -1;
    int myArry[N];
    myArry[0] = 1;
    myArry[1] = 23;
    myArry[2] = 3;
    myArry[3] = 4;
    myArry[4] = 5;
    myArry[5] = 22;
    myArry[6] = 7;
    myArry[7] = 8;
    myArry[8] = 9;
    myArry[9] = 22;
    myArry[10] = 12;


    if (do_dup_mal(myArry, 11, &prePos, &backPos) )
    printf("\nT
  • 相关阅读:
    Linux中常用操作命令(转)
    EntityFramework 优化建议(转)
    android 腾讯x5内核 浏览器
    Android MediaMetadataRetriever 读取多媒体文件信息,元数据(MetaData)
    Android MVP 利用rxjava 避免向Model传入监听方法
    Android 单元测试(junit、mockito、robolectric)
    Android 利用RecyclerView.Adapter刷新列表中的单个view问题
    android 5.0以上通知栏、状态栏图标变成白色
    Android 照相 滤镜
    Android 进程常驻(使用第三方MarsDaemon)(虽然不可用,但是还是保留下。)
  • 原文地址:https://www.cnblogs.com/relang99/p/1360773.html
Copyright © 2011-2022 走看看