1、題目描述:a,b兩個有序數組,找出第k小的數,logk,二分查找,1個小於怎么辦?
2、思路:
時間復雜度為O(log(m+n)),自然想到可能會用二分法
假設A 和B 的元素個數都大於k/2,我們將A 的第k/2 個元素(即A[k/2-1])和B 的第k/2個元素(即B[k/2-1])進行比較,有以下三種情況(為了簡化這里先假設k 為偶數,所得到的結論對於k 是奇數也是成立的):
• A[k/2-1] == B[k/2-1]
• A[k/2-1] > B[k/2-1]
• A[k/2-1] < B[k/2-1]
如果A[k/2-1] < B[k/2-1],意味着A[0] 到A[k/2-1] 的肯定在A 與B 的所有元素的前k個元素的范圍內,也就是說,A的前(k/2-1)個元素中不可能存在大於A與B所有元素的第k小元素。
因此,我們可以放心的刪除A 數組的這k/2 個元素。同理,當A[k/2-1] > B[k/2-1] 時,可
以刪除B 數組的k/2 個元素。
當A[k/2-1] == B[k/2-1] 時,說明找到了第k 大的元素,直接返回A[k/2-1] 或B[k/2-1]
即可。
因此,我們可以寫一個遞歸函數。那么函數什么時候應該終止呢?
• 當k=1 是,返回min(A[0], B[0]);
• 當A[k/2-1] == B[k/2-1] 時,返回A[k/2-1] 或B[k/2-1]
但是可能出現如果A中有3個元素,B中有12個元素,我們要找A與B所有元素的第8小元素,這時m < k/2
當出現這種情況時,我們可以取A中的所有元素,取B中的前k-m個元素
也可以按比例來取,比如取A中的前(m/(m+n))*k個元素,取B中前(k-(m/(m+n))*k)個元素
3、代碼:
public class Solution { public static void main(String[] args) { //定義兩個有序數組a,b int[] a = new int[]{1,9,10,18}; int[] b = new int[]{7,10}; //查找兩個數組的第k小元素 int k = 5; System.out.println(Search(a,0,a.length-1,b,0,b.length-1,k)); } public static int Search(int[] a,int startA,int endA,int[] b,int startB,int endB,int k){ if(k == 1){ if(a[startA] <= b[startB]){ return a[startA]; }else{ return b[startB]; } } //如果startA>endA,則說明A中的元素已經排除完,只剩下B中元素,此時只要取B中前k個元素就好 if(startA > endA){ return b[startB + k - 1]; } //同上 if(startB > endB){ return a[startA + k -1]; } int al = endA - startA + 1; int bl = endB - startB + 1; //如果a中元素個數大於b中元素個數,此時應該將a與b的位置交換,將元素少的數組放在前面,這是為了57行代碼的判斷 if(al > bl){ return Search(b,startB,endB,a,startA,endB,k); } int ms = 0; int ns = 0; //如果a中元素個數<k/2,此時應該取a中的所有元素,如果不做49的行的處理的話,可能會造成數組溢出 if((endA - startA + 1) < k/2){ ms = endA - startA + 1; }else{ ms = k/2; } ns = k - ms ; int m = ms-1 + startA; int n = ns-1+ startB; if(a[m] == b[n]){ return a[m]; }else if(a[m] > b[n]){ return Search(a,startA,m,b,n+1,endB,k-ns); }else{ return Search(a,m+1,endA,b,startB,n,k-ms); } } }