查找两个已经排好序的数组的第k大的元素
http://www.cnblogs.com/buptLizer/archive/2012/03/31/2427579.html
給出兩個排好序的數(shù)組 ,不妨設(shè)為a,b都按升序排列,及k的值,求出第k大的那個元素。
分析這個題目,如果題目沒有時間復(fù)雜度的要求,我們可以定義兩個指針i,j分別指向a,b,如果a[i]<b[j]則i++否則
j++,這個記錄下走了多少步,如果==k步,則找到了第k大的元素,復(fù)雜度為O(k).
那么如果有復(fù)雜度的要求,要求為O(log(len_a+len_b))呢,這個就得好好考慮,怎么利用二分來解決這個問題。
?判斷a[mid_a] 與 b[mid_b]的關(guān)系
?如果a[mida] < b[mid_b]
?1)k小于等于mida + midb + 1,那么b數(shù)組從mid_b開始就沒有用了,縮小b的搜索范圍
?2)k大于mida + midb + 1, 那么a數(shù)組從low到mid_a開始就沒用了,縮小a的搜索范圍
?3)終止條件是 a搜索完 返回b中元素或者相反
還有更霸氣的解答:
http://blog.csdn.net/beiyeqingteng/article/details/7533304
前言:
這道題是一道非常常見的面試題,也是一道能夠考察一個人的編程能力和算法的一道題。如果要求復(fù)雜度為 O(k), 是比較容易做出來的,但是,一般來講,面試官要求給出更低復(fù)雜度的算法。網(wǎng)上有很多不同的解法,但是,總的來講,那些程序考慮的因素太多,比較難懂,而且結(jié)構(gòu)很亂。在這里寫出自己的方法。本文的算法復(fù)雜度為 O(lg K)。?
PS. 如果你沒有見過這個題目,并且能夠在30分鐘內(nèi)寫出沒有bug的,復(fù)雜度為 O(lg K) 的程序,我愿意拜你為師,呵呵。
問題:
給定兩個已經(jīng)排序的數(shù)組(假設(shè)按照升序排列),然后找出第K小的數(shù)。比如數(shù)組A = {1, 8, 10, 20}, B = {5, 9, 22, 110}, 第 3 小的數(shù)是 8.
分析:
### 最好自己先動手寫一寫,然后再看后面的分析,因為自己寫過一遍以后,才能發(fā)現(xiàn)這個程序的難處在哪里。###
因為兩個數(shù)組已經(jīng)排序了,所以,要找出第 k 小的值,我們可以給每個數(shù)組設(shè)置一個“指針”, 比如pa, pb。在初始狀態(tài),兩個指針分別指向第一個起始值,即pa = 0, pb = 0。然后,我們開始進(jìn)行比較,如果A數(shù)組的值比B數(shù)組的值小, 把A數(shù)組的指針pa向前移動,然后再進(jìn)行比較。每次移動,我們都能夠得到當(dāng)前第 i 小的值(隨著pa,pb 的移動,i 會逐漸變大)。
比如對于數(shù)組A = {1, 8, 10, 20}, B = {5, 9, 22, 110},初始時,pa = 0; pb = 0,因為(A[pa] = 1) < (B[pb] = 5), 所以,第 1 小的值為 1,這個時候,我們會把 pa 向右移動,即 pa = 1, 并且 pb 保持不變: pb = 0. 然后再次比較A[pa] 和 B[pb] 的值,因為 ?(A[pa] = 8) > (B[pb] = 5), 所以,第 2 小的值是 5, 然后,我們增加 pb 的值。請注意,如果我們用一個變量來保存當(dāng)前第 i 小的值 (隨著pa, pb的移動,i 的值也會增加,那個變量保存的值也會改變)。那么,當(dāng)pa + pb = k 的時候,那么那個變量保存的一定是第 k 小的值。
這里要注意的是,當(dāng)一個數(shù)組的指針已經(jīng)“到頭”了,這個時候怎么進(jìn)行越界處理呢?如果我們用if 來處理,在程序里會有一大堆的 if 判斷語句,太難看了。我們這里可以用一個簡單的判斷語句就可以處理了。
[java]?view plaincopy
好了,有了上面的分析,下面的代碼就不難理解了。
[java]?view plaincopy
上面這個程序的復(fù)雜度是O(k),分析很簡單,就不再講了。
現(xiàn)在再來看如何得到 O(lg K) 的算法。
在上面的程序里,我們是逐一比較數(shù)組A 和數(shù)組B的值,但是,這樣做還是太慢了,因為兩個數(shù)組是已經(jīng)排過序的,我們完全可以利用二分查找的方法來解決這樣的問題。但是具體介紹怎么做之前,先講一些預(yù)備知識。
對于數(shù)組A 、 B , 如果 B[pb] < A[pa] && B[pb] > A[pa - 1], 那么 B[pb] 一定是第 pa + pb + 1 ?小的數(shù)。 比如數(shù)組A = {1, 8, 10, 20}, B = {5, 9, 22, 110},?pa = 2, pb = 1, 這時,(B[pb] = 9) < (A[pa] =10) && (B[pb] = 9) > (A[pa - 1] = 8) ,那么,B[pb] = 9 一定是第 pa+pb+1 = 4 小的數(shù)。 換一句話說,如果我們要找第 k 小的數(shù),那么, pa + pb ?= k - 1。而且,B[pb] < A[pa] && B[pb] > A[pa - 1] 或者?A[pa] < B[pb] && A[pa] > B[pb - 1] 中的其中一個必須成立。 如果不成立, 我們需要移動pa 和 pb 的位置來使得其中的一個式子成立 (參看代碼)。這是本題算法的本質(zhì)。
但是,這里也會出現(xiàn)”邊界“問題,比如,k = 1, 那么 pa + pb ?= 1 - 1, 即 pa + pb = 0. 因為 pa >= 0, pb >=0, 所以,我們得到 pa = 0, pb = 0. 那么這樣的話, 求A [pa-1]值的時候 就會有exception. 同理,對于數(shù)組A = {1, 8, 10, 20}, B = {5, 9, 22, 110},當(dāng)k = 8 的時候, pa + pb ?= 8 - 1, 即 pa + pb = 7, 但是,pa <= 3, pb <= 3。邊界的處理是這道題最最麻煩的地方。對于這個問題,我們用下面這段代碼來解決。
[java]?view plaincopy
有了上面的分析,代碼就容易寫出來了。
在程序里,我們設(shè)置 pa 的初始值為Math.min(A.length, k - 1),然后,通過增加或者減少pa 的值,來使得 B[pb] < A[pa] && B[pb] > A[pa - 1] 或者?A[pa] < B[pb] && A[pa] > B[pb - 1] ?成立,代碼中, delta 指的是 pa 的變化量,每次遞歸以后,delta的值變成一半。 在程序中用delta完成二分查找
public class FindKthSmallestValue {public static int findKthSmallest(int[] A, int[] B, int pa, int delta, int k) {int pb = (k - 1) - pa;//保證pb+ba = k -1int Ai_1 = ((pa == 0) ? Integer.MIN_VALUE : A[pa-1]);int Bj_1 = ((pb == 0) ? Integer.MIN_VALUE : B[pb-1]);int Ai = ((pa == A.length) ? Integer.MAX_VALUE : A[pa]);int Bj = ((pb == B.length) ? Integer.MAX_VALUE : B[pb]);//滿足其中之一條件,就返回if (Bj_1 <= Ai && Ai <= Bj) return Ai;if (Ai_1 <= Bj && Bj <= Ai) return Bj;//delta表示pa的變化量(pa通過加減delta實現(xiàn)pa的增加或者減少)//如果 Ai > Bj, 我們要縮小pa的值,即 pa = pa - delta//因為 pb = (k - 1) - pa, 所以,如果delta的值太大,//pa會變得很小,因而 可能會導(dǎo)致 pb > B.length. 所以需要處理一下。// 對于pa = pa + delta 的處理也是一樣if (Ai > Bj) {pa = ((k - 1) - (pa - delta) > B.length) ? k - 1 - B.length : pa - delta;//防止把pa調(diào)整的過小,而導(dǎo)致pb+pa<k-1return findKthSmallest(A, B, pa, (delta + 1) / 2, k);} else {pa = (pa + delta > A.length) ? A.length : pa + delta;return findKthSmallest(A, B, pa, (delta + 1) / 2, k); }}public static void main(String[] args) {int[] A = {1, 8, 8, 10, 20};int[] B = {5, 8, 8, 9, 22, 110};int k = 7;int pa = Math.min(A.length, k - 1);System.out.println(findKthSmallest(A, B, pa, (pa + 1) / 2, k));} }
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖
總結(jié)
以上是生活随笔為你收集整理的查找两个已经排好序的数组的第k大的元素的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 百度vs腾讯
- 下一篇: 短址(short URL)原理及其实现