數組中的趣味題(二)


  過了好幾天,今天繼續上一篇:數組中的趣味題(一)。文章的內容主要來自本人看過的書籍和其他相關博文,文章內容已經對網上查到的相關資料進行了部分修改,全當作為自己的復習資料吧。當然了,文中難免出現錯誤,希望大家能幫忙指出來,本人一直希望網友能一起探討問題,一起指出文中的問題,改正過來,這樣看到文章的人才能收獲正確的知識,實際上幫助別人的過程也正是自己提高的過程。最近碰到幾個這樣的人,發這樣的評論:“這文章真不怎么地”,“文章中有各種問題啊,這怎么能行”,“哎,樓主還需要提高啊”。說實話,不是樓主度量低,您就指出問題在哪不就好了嘛,大家看到都會感激你,為什么總是說這種模凌兩可的話,讓人不知道從哪里下手,如果您不想幫忙改正錯誤,那就不知道您的真正用意是什么了?不要發這種話,沒什么意義。您要是不喜歡別人的文章也可以自己寫。。。千萬別這樣發評論,做個高素質的人!!!

1.找出數組中唯一的重復元素


問題描述:給定包含101個元素的數組arr,數組中元素一定屬於[1,100],並且[1,100]之間的每個數都在arr中出現過。

解決方案:分析,數組中有101個元素,如果[1,100]之間的每個數出現一次,那就是100個元素了,剩下的那個元素就是唯一的重復出現的元素。我們可以通過遍歷arr,得到arr中所有元素的總和,然后既然[1,100]之間的每個數出現一次,那么我們減去1+2+……+99+100的和,結果就是我們需要的唯一的重復出現的元素了。時間復雜度是O(n)。

代碼:

FindOnlyRepeat
 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)。

代碼:

Find
 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的大小。

代碼:

FindTheSum
 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)。

代碼:

MaxSubSum
 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)

代碼:

MaxSubMult
 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)

代碼:

Deal
 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]不同號,則絕對值最小數應該是“最小的正整數”和“最大的負整數”中的一個。關鍵就是找到這“最小的正整數”和“最大的負整數”。既然數組是已經排好序了,那么可以利用二分。

代碼:

MinAbs
 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】《編程之美》

 

 學習中的一點總結,歡迎拍磚^^

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM