程式員面試100題之十:快速尋找滿足條件的兩個數

來源:互聯網
上載者:User

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

     假如有如下的兩個數組,:

    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),下面這個程式就利用了這個思想,代碼如下所示:

int getSumNum(int[] arr,int Sum),   //arr為數組,Sum為和 {int i,j;for(i = 0, j = n-1; i < j ; ){if(arr[i] + arr[j] == Sum)return ( i , j );else if(arr[i] + arr[j] < Sum)i++;elsej--;}return ( -1 , -1 );}

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

      剛開始一直無法理解這樣一定可以找到這個和嗎?難道不會漏掉瞭解的位置。可以這麼理解,假如排好序後的數組為1,3,6,a,9,12,17,28,b,35,46  ,那麼i最初指向1的位置,j最初指向46的位置,比如所求的是Sum=a+b,a<b,a和b在數組中的某位置上。那麼i和j在變化過程中,只考慮i遇到了a或者j遇到了b的時候,必定有一個先遇到,比如i遇到了a,那麼這個時候j必定還沒指到b,故這是j指到的值比b大,從而j減小直到b位置。同理若j先指到了b位置,那麼i必定還沒指到a(這是我們的前提),然後i現在指到的值比a小,故i增加,直到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]也要快。


 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.