1. 程式人生 > >Leetcode Array 4 Median of Two Sorted Arrays

Leetcode Array 4 Median of Two Sorted Arrays

部分 存在 滿足 find cti itl title 要去 double

做leetcode題目的第二天,我是按照分類來做的,做的第一類是Array類,碰見的第二道題目,也就是今天做的這個,題目難度為hard。題目不難理解,但是要求到了時間復雜度,就需要好好考慮使用一下算法了。剛開始沒啥思路,就用暴力的方法,用雙層循環遍歷的一下兩個已經排好序的數組 ,在中間位置停止找道中位數。這樣時間復雜度是肯定不能滿足題目要求的,但是程序測試還是過了。

苦於自己沒有思路,又不甘心就這樣水過一道題,還是搜了一下博客,膜拜了一下大神。最好的方法是將中位數 -- 兩個數組數據排好序之後的(m+n)/2 位置左右的數(奇,偶)(程序裏面會有體現)--看成是求排好序的數組的第k = (m+n)/2 小的數據,然後采用類似於二分的方法求解。

在這裏先把題目粘過來:

There are two sorted arrays nums1 and nums2 of size m and n respectively.

Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

Example 1:

1 nums1 = [1, 3]
2 nums2 = [2]
3 
4 The median is 2.0


Example 2:

1 nums1 = [1, 2]
2 nums2 = [3, 4]
3 4 The median is (2 + 3)/2 = 2.5

這裏先把我的java代碼貼一下:(給自己個警示)

 1 public class Solution {
 2     public double findMedianSortedArrays(int[] nums1, int[] nums2) {
 3         int len = nums1.length+nums2.length;
 4         int []ans_c = new int[len];
 5         boolean flag = false;
 6         int i=0,j=0
; 7 int k=0; 8 for(;k<len/2+1;k++){ 9 if(i<nums1.length && j<nums2.length){ 10 if(nums1[i]<=nums2[j]){ 11 ans_c[k]=nums1[i]; 12 i++; 13 } 14 else{ 15 ans_c[k]=nums2[j]; 16 j++; 17 } 18 } 19 else if(i<nums1.length){ 20 ans_c[k]=nums1[i]; 21 i++; 22 } 23 else{ 24 ans_c[k]=nums2[j]; 25 j++; 26 } 27 } 28 if(len%2==0){ 29 return (double)(ans_c[k-1]+ans_c[k-2])/2; 30 } 31 else{ 32 return (double)ans_c[k-1]; 33 } 34 } 35 }

C++ 代碼:

這裏對於求解思路呢我就不做過多的說明了,把別人的博客鏈接發過來自己欣賞一下吧,我想做的是對自己寫的代碼進行一下註釋說明,以便自己之後回憶起來好理解。

這裏摘錄一下他的分析過程

技術分享
 1 最後從medianof two sorted arrays中看到了一種非常好的方法。原文用英文進行解釋,在此我們將其翻譯成漢語。該方法的核心是將原問題轉變成一個尋找第k小數的問題(假設兩個原序列升序排列),這樣中位數實際上是第(m+n)/2小的數。所以只要解決了第k小數的問題,原問題也得以解決。
 2 
 3 首先假設數組A和B的元素個數都大於k/2,我們比較A[k/2-1]和B[k/2-1]兩個元素,這兩個元素分別表示A的第k/2小的元素和B的第k/2小的元素。這兩個元素比較共有三種情況:>、<和=。如果A[k/2-1]<B[k/2-1],這表示A[0]到A[k/2-1]的元素都在A和B合並之後的前k小的元素中。換句話說,A[k/2-1]不可能大於兩數組合並之後的第k小值,所以我們可以將其拋棄。
 4 
 5 證明也很簡單,可以采用反證法。假設A[k/2-1]大於合並之後的第k小值,我們不妨假定其為第(k+1)小值。由於A[k/2-1]小於B[k/2-1],所以B[k/2-1]至少是第(k+2)小值。但實際上,在A中至多存在k/2-1個元素小於A[k/2-1],B中也至多存在k/2-1個元素小於A[k/2-1],所以小於A[k/2-1]的元素個數至多有k/2+ k/2-2,小於k,這與A[k/2-1]是第(k+1)的數矛盾。
 6 
 7 當A[k/2-1]>B[k/2-1]時存在類似的結論。
 8 
 9 當A[k/2-1]=B[k/2-1]時,我們已經找到了第k小的數,也即這個相等的元素,我們將其記為m。由於在A和B中分別有k/2-1個元素小於m,所以m即是第k小的數。(這裏可能有人會有疑問,如果k為奇數,則m不是中位數。這裏是進行了理想化考慮,在實際代碼中略有不同,是先求k/2,然後利用k-k/2獲得另一個數。)
10 
11 通過上面的分析,我們即可以采用遞歸的方式實現尋找第k小的數。此外我們還需要考慮幾個邊界條件:
12 
13     如果A或者B為空,則直接返回B[k-1]或者A[k-1];
14     如果k為1,我們只需要返回A[0]和B[0]中的較小值;
15     如果A[k/2-1]=B[k/2-1],返回其中一個;
分析

這裏我補充說明幾個問題:

第一是兩個數組長度之和是有奇、偶之分的,奇數時我們需要求解第 k=(m+n)/2 + 1 小數據 ,偶數時我們需要求解 第 k1 = (m+n) / 2 和第 k2=(m+n)/2+1

小,然後 k = (k1+k2)/2

第二是這兩個數組並沒有合並,我們需要兩個沒有完全排好序的數組的第k小數,可以分別在兩個數組中求第 k/2 小的數進行比較,這就和上面粘貼的分析相結合了。

第三是有可能其中一個數組長度較小,小於 k/2,這是我們就需要在程序中進行判斷了,每次都將長度較小的數組置於,傳遞參數的第一個位置,然後江數組長度與 k/2 比較,如果m>k/2,就直接選取第 pa = k/2小數據,不然就取第一個數組的長度 pa = m,pb = k-pa,然後將A[pa-1]於B[pb-1](第pb小對應數組的位置應該為pb-1)進行比較,這裏痛上面的分析過程

代碼:

 1 class Solution {
 2 public:
 3     double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
 4         int total = nums1.size()+nums2.size();
 5         if(total%2 == 0){                      //判斷奇偶
 6             return (finKth(nums1,nums2,total/2)+finKth(nums1,nums2,total/2+1))/2;
 7         }
 8         else{
 9             return finKth(nums1,nums2,total/2+1);
10         }
11     }
12 public:
13     double finKth(vector<int> nums1,vector<int> nums2,int k){  // 尋找第k小
14         if( nums1.size() > nums2.size()){             // 將nums1永遠作為長度較小的數組
15             return finKth(nums2,nums1,k);
16         }
17         if( nums1.size() == 0 ){                      //如果其中一個數組長度為0則第k小就是nums2的第k小
18             return nums2[k-1];
19         }
20         if(k == 1){                                   //k=1 說明  一種情況是 兩個數組長度都為 1 這是 需要求 (1+1)/2 直接返回兩個數組中較小的一個
21             return nums1[0]<nums2[0]?nums1[0]:nums2[0];   //      另一種情況是 每次遞歸都會去除一部分元素,使得第k小變成第k-p小 當k-p為一時,就可以返回,兩個數組nums1[0],nums2[0]中較小的一個了 
22         }
23         int findKsa = nums1.size() <k/2 ? nums1.size() : k/2;
24         int findKsb = k-findKsa;
25         if(nums1[findKsa-1]<nums2[findKsb-1]){
26             return finKth(vector<int>(nums1.begin()+findKsa,nums1.end()),nums2,k-findKsa);
27         }   //對這裏的vector向量的傳遞做個說明:因為每次都需要去除一部分數據 ,所以使用 vector 的 begin 和 end 方法截取中間的一部分元素
28         else if(nums1[findKsa-1]>nums2[findKsb-1]){
29             return finKth(nums1,vector<int>(nums2.begin()+findKsb,nums2.end()),k-findKsb);
30         }
31         else{
32             return nums1[findKsa-1];
33         }
34     }
35 };

在這裏說說自己做題的感覺,每次看到題都沒有思路,做題只是能看懂別人的思路和代碼········希望自己逐漸進步,直到可以獨立作出大部分的題目(可以自己獨立寫出自己的的思路)

Leetcode Array 4 Median of Two Sorted Arrays