[面試題]設計一個算法找到數組中兩個元素相加等於指定數的所有組合


http://blog.csdn.net/lalor/article/details/7554594

思路1:可以用hash表來存儲數組中的元素,這樣我們取得一個數后,去判斷sum - val 在不在數組中,如果在數組中,則找到了一對二元組,它們的和為sum,該算法的缺點就是需要用到一個hash表,增加了空間復雜度。

思路2:同樣是基於查找,我們可以先將數組排序,然后依次取一個數后,在數組中用二分查找,查找sum -val是否存在,如果存在,則找到了一對二元組,它們的和為sum,該方法與上面的方法相比,雖然不用實現一個hash表,也沒不需要過多的空間,但是時間多了很多。排序需要O(nLogn),二分查找需要(Logn),查找n次,所以時間復雜度為O(nLogn)。

思路3:該方法基於第2種思路,但是進行了優化,在時間復雜度和空間復雜度是一種折中,但是算法的簡單直觀、易於理解。首先將數組排序,然后用兩個指向數組的指針,一個從前往后掃描,一個從后往前掃描,記為first和last,如果 fist + last < sum 則將fist向前移動,如果fist + last > sum,則last向后移動。

 

  1. #include <iostream>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.   
  5. void printPairSums(int data[], int size, int sum);  
  6. int main(int argc, char* argv[])  
  7. {  
  8.     int data[] = {1, 5, 9, -1, 4, 6, -2, 3, -8};  
  9.     int size = sizeof(data) / sizeof(data[0]);  
  10.     int i;  
  11.     sort(data, data + size);  
  12.     printPairSums(data, size, 8);  
  13.   
  14.     return 0;  
  15. }  
  16. void printPairSums(int data[], int size, int sum)  
  17. {  
  18.     int first = 0;  
  19.     int last = size -1;  
  20.     int s = 0;  
  21.     while (first < last)  
  22.     {  
  23.         s = data[first] + data[last];  
  24.         if (s == sum)  
  25.         {  
  26.             cout << data[first] << " + " << data[last] << " = " << sum << endl;  
  27.             first++;  
  28.             last--;  
  29.         }  
  30.         else if (s < sum)  
  31.         {  
  32.             first++;  
  33.         }  
  34.         else  
  35.         {  
  36.             last--;  
  37.         }  
  38.     }  
  39. }  



參考資料:http://www.geeksforgeeks.org/archives/484

 

============================================

http://blog.csdn.net/hackbuteer1/article/details/6699642
 

能否快速找出一個數組中的兩個數字,讓這兩個數字之和等於一個給定的值,為了簡化起見,我們假設這個數組中肯定存在至少一組符合要求的解。

     假如有如下的兩個數組,如圖所示:

    5,6,1,4,7,9,8

    給定Sum= 10

    1,5,6,7,8,9

    給定Sum= 10

   分析與解法

    這個題目不是很難,也很容易理解。但是要得出高效率的解法,還是需要一番思考的。

    解法一

     一個直接的解法就是窮舉:從數組中任意取出兩個數字,計算兩者之和是否為給定的數字。

     顯然其時間復雜度為N(N-1)/2即O(N^2)。這個算法很簡單,寫起來也很容易,但是效率不高。一般在程序設計里面,要盡可能降低算法的時間和空間復雜度,所以需要繼續尋找效率更高的解法。

     解法二

     求兩個數字之和,假設給定的和為Sum。一個變通的思路,就是對數組中的每個數字arr[i]都判別Sum-arr[i]是否在數組中,這樣,就變通成為一個查找的算法。

     在一個無序數組中查找一個數的復雜度是O(N),對於每個數字arr[i],都需要查找對應的Sum-arr[i]在不在數組中,很容易得到時間復雜度還是O(N^2)。這和最原始的方法相比沒有改進。但是如果能夠提高查找的效率,就能夠提高整個算法的效率。怎樣提高查找的效率呢?

     學過編程的人都知道,提高查找效率通常可以先將要查找的數組排序,然后用二分查找等方法進行查找,就可以將原來O(N)的查找時間縮短到O(log2N),這樣對於每個arr[i],都要花O(log2N)去查找對應的Sum-arr[i]在不在數組中,總的時間復雜度降低為N* log2N。當讓將長度為N的數組進行排序本身也需要O(N*log2N)的時間,好在只須要排序一次就夠了,所以總的時間復雜度依然是O(N*log2N)。這樣,就改進了最原始的方法。

     到這里,有的讀者可能會更進一步地想,先排序再二分查找固然可以將時間從O(N^2)縮短到O(N*log2N),但是還有更快的查找方法:hash表。因為給定一個數字,根據hash表映射查找另一個數字是否在數組中,只需要O(1)時間。這樣的話,總體的算法復雜度可以降低到O(N),但這種方法需要額外增加O(N)的hash表存儲空間。某些情況下,用空間換時間也不失為一個好方法。

     解法三

     還可以換個角度來考慮問題,假設已經有了這個數組的任意兩個元素之和的有序數組(長為N^2)。那么利用二分查找法,只需用O(2*log2N)就可以解決這個問題。當然不太可能去計算這個有序數組,因為它需要O(N^2)的時間。但這個思考仍啟發我們,可以直接對兩個數字的和進行一個有序的遍歷,從而降低算法的時間復雜度。

      首先對數組進行排序,時間復雜度為(N*log2N)。

      然后令i = 0,j = n-1,看arr[i] + arr[j] 是否等於Sum,如果是,則結束。如果小於Sum,則i = i + 1;如果大於Sum,則 j = j – 1。這樣只需要在排好序的數組上遍歷一次,就可以得到最后的結果,時間復雜度為O(N)。兩步加起來總的時間復雜度O(N*log2N),下面這個程序就利用了這個思想,代碼如下所示:

[cpp]  view plain copy
  1. int getSumNum(int[] arr,int Sum),   //arr為數組,Sum為和   
  2. {  
  3.     int i,j;  
  4.     for(i = 0, j = n-1; i < j ; )  
  5.     {  
  6.         if(arr[i] + arr[j] == Sum)  
  7.             return ( i , j );  
  8.         else if(arr[i] + arr[j] < Sum)  
  9.             i++;  
  10.         else  
  11.             j--;  
  12.     }  
  13.     return ( -1 , -1 );  
  14. }  

      它的時間復雜度是O(N)。

      剛開始一直無法理解這樣一定可以找到這個和嗎?難道不會漏掉了解的位置。可以這么理解,假如排好序后的數組為1,3,6,a,9,12,17,28,b,35,46  ,那么i最初指向1的位置,j最初指向46的位置,比如所求的是Sum=a+b,a

 

 擴展問題

1、如果把這個問題中的“兩個數字”改成“三個數字”或“任意個數字”時,你的解是什么呢?

三個數字:首先還是先對數組進行排序,然后從i=0到n-1進行遍歷,遍歷arr[i]時,在調用上面的函數getSumNum(arr , Sum-arr[i])即可。

任意m個數字的想法:

      首先還是先對數組進行排序,然后從i=0到n-1個元素遍歷,遍歷arr[i]時,在剩下的n-1個元素中調用getSumNum(arr,Sum-arr[i]),此時為求m-1個元素和為Sum-arr[i];接下來,同樣的方法,從j=0到n-2個元素遍歷,遍歷arr[j]時在arr上遞歸調用getSumNum(arr,Sum-arr[i]-arr[j]),此時為求m-2個元素和為Sum-arr[i]-arr[j];依次遞歸,直到為求2個元素和為Sum-?-?-?...時為止。

不論是求3個數字還好是m個數字,總是能比較窮舉法少一個數量級n,比先排序然后二分查找求Sum-arr[i]也要快。


免責聲明!

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



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