數組a[N],1至N-1這N-1個數存放在a[N]中,其中某個數重複一次。寫一個函數,找出被重複的數字。時間複雜度必須爲o(N)函數原型:算法
int do_dup(int a[],int N)數組
××××××××××××××××××××××××××××××××××ide
假金條的數學思想函數
此算法題借鑑了假金條的思想,不過比那個過程簡單,存放1至N-1,就至關於依次從各個袋中取出1至N-1個金條,可是有一個是重的,計算這N個數的和將至關於稱總重量,減去1至N-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等各方法的思路是一致的,即訪問事後作標記,再次訪問時便可知道是否重複。若是考慮空間複雜度的話,其空間o(N)
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],裏面是1至N-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];
}
}
時間複雜度o(n) 空間複雜度o(1)
不過期間複雜度o(n) 空間複雜度o(1)不僅一個重複利用此法也是能夠實現的