過了好幾天,今天繼續上一篇:數組中的趣味題(一)。文章的內容主要來自本人看過的書籍和其他相關博文,文章內容已經對網上查到的相關資料進行了部分修改,全當作為自己的復習資料吧。當然了,文中難免出現錯誤,希望大家能幫忙指出來,本人一直希望網友能一起探討問題,一起指出文中的問題,改正過來,這樣看到文章的人才能收獲正確的知識,實際上幫助別人的過程也正是自己提高的過程。最近碰到幾個這樣的人,發這樣的評論:“這文章真不怎么地”,“文章中有各種問題啊,這怎么能行”,“哎,樓主還需要提高啊”。說實話,不是樓主度量低,您就指出問題在哪不就好了嘛,大家看到都會感激你,為什么總是說這種模凌兩可的話,讓人不知道從哪里下手,如果您不想幫忙改正錯誤,那就不知道您的真正用意是什么了?不要發這種話,沒什么意義。您要是不喜歡別人的文章也可以自己寫。。。千萬別這樣發評論,做個高素質的人!!!
1.找出數組中唯一的重復元素
問題描述:給定包含101個元素的數組arr,數組中元素一定屬於[1,100],並且[1,100]之間的每個數都在arr中出現過。
解決方案:分析,數組中有101個元素,如果[1,100]之間的每個數出現一次,那就是100個元素了,剩下的那個元素就是唯一的重復出現的元素。我們可以通過遍歷arr,得到arr中所有元素的總和,然后既然[1,100]之間的每個數出現一次,那么我們減去1+2+……+99+100的和,結果就是我們需要的唯一的重復出現的元素了。時間復雜度是O(n)。
代碼:

1 //找到數組中唯一的重復元素 2 int FindOnlyRepeat(int *arr) 3 { 4 int tmp_sum = 0;//記錄1~100的和 5 int total_sum = 0;//數組中所有樹的和 6 int i = 0; 7 while(i < 101) 8 { 9 tmp_sum += i+1; 10 total_sum += arr[i++]; 11 } 12 tmp_sum-=101; 13 return total_sum - tmp_sum; 14 }
2.找出數組中出現奇數次的元素
問題描述:現在有一個整數數組arr,其中的元素只有一個元素出現奇數次,請找出這個元素。
解決方案:對於任意一個數k,有k^k = 0,k^0 = k,所以將arr中所有元素進行異或,那么出現次數為偶數的元素異或后都變成了0,出現次數為奇數的元素異或后得到元素本身。下面寫代碼就很容易了~~時間復雜度是O(n)。
代碼:

1 //找出數組唯一的出現次數是奇數的元素 2 int Find(int *arr,int n) 3 { 4 int ans = arr[0]; 5 for(int i = 1 ; i < n ; i++) 6 { 7 ans^=arr[i]; 8 } 9 return ans; 10 }
3.找出兩個數組中滿足給定和的數對
問題描述:有兩個數組arr1和arr2,兩個數組均已經排好序,現在要找出這樣的x和y使得x+y=sum,其中sum已知,x是arr1中某個元素,y是arr2中的某個元素。
解決方案:既然數組arr1和arr2中的元素已經排好序了,那么問題就簡單了不少。假設arr1[i] + arr2[j] > sum,若arr1下標不變,那么要找到滿足條件的x和y,只能取arr2中下標< j 的元素和arr1[i]相加,結果才可能等於sum;假設arr1[i] + arr2[j] < sum,若arr2的下標不變,那么要找到滿足條件的x和y,只能取arr1中下標 > i 的元素和arr2[j]相加,結果才可能等於sum。因此我們可以設置兩個指針 i 和 j ,分別指向arr1的開始位置和結束位置,這時 i 只能不斷變大,j 只能不斷變小,直到超過了數組的范圍。通過代碼更好理解,看代碼吧~~時間復雜度是O(n+m),n和m分別是arr1和arr2的大小。
代碼:

1 //找出數組中兩個數的和是固定值 2 void FindTheSum(int *arr1,int *arr2,int n,int m,int sum) 3 { 4 int i = 0 , j = m-1; 5 while(i < n && j >= 0) 6 { 7 if(arr1[i] + arr2[j] > sum) 8 { 9 j--; 10 } 11 else if(arr1[i] + arr2[j] < sum) 12 { 13 i++; 14 } 15 else 16 { 17 cout<<arr1[i]<<" + "<<arr2[j]<<" = "<<sum<<endl; 18 i++; 19 j--; 20 } 21 } 22 }
4.最大子段和
問題描述:對於整數數組arr,求出最大連續子段之和,字段和是這樣的: i 和 j 是數組下標並且i <= j ,那么sub_sum = arr[i] + arr[i+1]……+arr[j],就是一段子段和,我們的目的是找到最大的子段和。如果和為負數,按0計算。
解決方案:這個算法,可是算得上是比較經典的算法了,對照着代碼,理解一下思路吧:MAX是記錄在搜索過程中出現的最大值,也就是最終的最大值保存的地方,而對於tmp的話,tmp > 0,就說明如果tmp繼續和后面的數(假設是arr[i])相加,就能繼續使得以a[i]結尾的子段和變大,而當tmp < 0 ,就說明這個值不能加到a[i]上,這一定會導致總和不是最大的,tmp的意圖比較像是獲得“最大前綴和”!!我就說這么多吧,這個東西還是看代碼理解吧!!時間復雜度是O(n)。
代碼:

1 //求最大子段和 2 int MaxSubSum(int *arr,int n) 3 { 4 int tmp = 0; 5 int MAX = arr[0]; 6 for(int i = 0 ; i < n ; i++) 7 { 8 tmp += arr[i]; 9 if(tmp < 0) 10 { 11 tmp = 0; 12 } 13 if(MAX < tmp) 14 { 15 MAX = tmp; 16 } 17 } 18 return MAX; 19 }
5.最大子段積
問題描述:對於整數數組arr,求出最大連續子段積。具體什么是最大連續子段積,我想參考最大字段和應該能知道。
解決方案:和最大子段和的思想和很相似,看代碼!時間復雜度是O(n)
代碼:

1 int MaxSubMult(int *arr,int n) 2 { 3 int min_Mult = 1; 4 int max_Mult = 1; 5 int ans = arr[0]; 6 for(int i = 0 ; i < n ; i++) 7 { 8 if(arr[i] > 0) 9 { 10 max_Mult *= arr[i]; 11 min_Mult = min_Mult*arr[i] < 1 ? min_Mult*arr[i] : 1; 12 } 13 else if(arr[i] < 0) 14 { 15 int tmp = max_Mult; 16 max_Mult = min_Mult*arr[i] > 1 ? min_Mult*arr[i] : 1; 17 min_Mult = tmp*arr[i]; 18 } 19 else //arr[i] = 0 20 { 21 max_Mult = 1; 22 min_Mult = 1; 23 } 24 if(max_Mult > ans) 25 { 26 ans = max_Mult; 27 } 28 } 29 return ans; 30 }
6.一種特殊的排序(重排問題)
問題描述: 對於整數數組arr中包含0和非0元素,現在要對數組進行處理,使得處理后的結果滿足:(1)0都排在數組的前面(2)非0元素都在數組的后面,並且相對順序不能變化。例如arr[] = {3,0,1,0},經處理后的結果應為:{0,0,3,1}。
解決方案:要完成這個要求,只需要從后向前遍歷數組arr,遇到一個非0的數就將其放到數組的后面,遇到的第一個非0元素放在數組的第n-1位置,遇到的第二個非0元素放在數組的第n-2位置(其中n是數組的大小)。這樣就完成了題目的要求!!!時間復雜度是O(n)
代碼:

1 void Deal(int *arr,int n) 2 { 3 int tmp = n-1; 4 for(int i = n-1 ; i >= 0 ; i--) 5 { 6 if(arr[i]) 7 { 8 if(!arr[tmp]) 9 { 10 arr[tmp] = arr[i]; 11 arr[i] = 0; 12 } 13 tmp--; 14 } 15 } 16 }
7.找出絕對值最小的數
問題描述:整數數組arr已經排好序,其中包含正數,0負數,數組大小是n,要求找到絕對值最小的數。
解決方案:通過分析有幾種情況:(1)若arr[0]和arr[n-1]同號且arr[0]>=0,則絕對值最小的數是arr[0];(2)若arr[0]和arr[n-1]同號且arr[0]<=0,則絕對值最小的數是arr[n-1];(3)若arr[0]和arr[n-1]不同號,則絕對值最小數應該是“最小的正整數”和“最大的負整數”中的一個。關鍵就是找到這“最小的正整數”和“最大的負整數”。既然數組是已經排好序了,那么可以利用二分。
代碼:

1 int MinAbs(int *arr, int n) 2 { 3 if(n == 1) //只有一個元素 4 { 5 return arr[0] ; 6 } 7 if(arr[0]*arr[n-1] >= 0) 8 { 9 return arr[0] >=0 ? arr[0] : arr[n -1] ; 10 } 11 int l =0 ; 12 int r = n - 1; 13 while(l < r) 14 { 15 cout<<l<<" "<<r<<endl; 16 if(l + 1 == r)//出口,只剩下兩個元素,可以得到結果了 17 { 18 return abs(arr[l]) < abs(arr[r]) ? abs(arr[l]) : abs(arr[r]); 19 } 20 int m = (l + r) / 2; 21 if(arr[m] * arr[r] >= 0) 22 { 23 r = m; 24 continue; 25 } 26 if(arr[l] * arr[m] >= 0) 27 { 28 l = m; 29 continue; 30 } 31 } 32 }
文章參考資料:
【1】博文http://www.cnblogs.com/graphics/archive/2010/08/24/1761620.html#link13
【2】《編程之美》
學習中的一點總結,歡迎拍磚^^