查找數組中重複的數字

 數組a[N]1N-1N-1個數存放在a[N]中,其中某個數重複一次。寫一個函數,找出被重複的數字。時間複雜度必須爲oN函數原型:算法

int do_dup(int a[],int N)數組

 

××××××××××××××××××××××××××××××××××ide

假金條的數學思想函數

 

此算法題借鑑了假金條的思想,不過比那個過程簡單,存放1N-1,就至關於依次從各個袋中取出1N-1個金條,可是有一個是重的,計算這N個數的和將至關於稱總重量,減去1N-1的和(用數學方法求)就可求出來重複的數。總之要充分利用數學知識性能

 

const int N = 5;優化

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

int tmp1 = 0;rest

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("\n
相關文章
相關標籤/搜索