尋找兩個有序數組的中位數


題目描述:
給定兩個大小為 m 和 n 的有序數組 nums1 和 nums2。
請你找出這兩個有序數組的中位數,並且要求算法的時間復雜度為 O(log(m + n))。
你可以假設 nums1 和 nums2 不會同時為空。

示例 1:
  nums1 = [1, 3]
  nums2 = [2]
  則中位數是 2.0

示例 2:
  nums1 = [1, 2]
  nums2 = [3, 4]
  則中位數是 (2 + 3)/2 = 2.5

 

題目解答:
方法1:暴力法
  重新申請一個m + n長的數組,將兩個數組的元素按從小到大的順序放到新數組中,然后直接求中位數,但時間復雜度為O(m + n),不符合題目要求。運行時間也可以到達最快24ms,代碼如下。

 1 double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
 2     int m = nums1Size, n = nums2Size;
 3     int* t = (int*)malloc((m + n) * sizeof(int));
 4     int i = 0, j = 0, index = 0;
 5     int left = 0, right = 0;
 6     double result = 0;
 7     while(index < m + n) {
 8         left = (i < m ? nums1[i] : INT_MAX);
 9         right = (j < n ? nums2[j] : INT_MAX);
10         if(left < right)
11             t[index++] = nums1[i++];
12         else 
13             t[index++] = nums2[j++];
14     }
15     if((m + n) & 1)
16         result =  t[(m + n) / 2];
17     else
18         result = (t[(m + n) / 2] + t[(m + n) / 2 - 1]) / 2.0 ;
19     free(t);
20     return result;
21 }


方法2:二分查找
  易知二分法或者二叉樹相關算法的時間復雜度為O(log(n)),而題目要求O(log(m + n)),則可能會用到其中一個,沒有二叉樹,所以本題可能就是用二分法。問題就是確定子問題。
  根據中位數的定義,中位數的左右兩側數字個數相同,且其左邊的數字比其小,右邊的數字比其大。構造兩個子集,分別是左右子集,假定取nums1中的前i (i ∈[0, m])個放在左子集,nums2的前j (j ∈[0, n])個放在左子集,此時左子集數字個數為i + j,右子集數字個數為m + n - i - j。關系是:

i與j關系

m + n情況 

中位數
i + j = m + n - i - j

m + n 為偶數

中位數為左側最大值與右側最小值的平均值, (max_left + min_right) / 2
i + j = m + n + 1 - i - j 

m + n 為奇數

中位數放在左子集,即左側最大值 max_left

 

  假設我們遍歷 i,偶數時為j = (m + n) / 2 - i,奇數時為j = (m + n + 1) / 2 - i,但如果m > n,j會為負數,所以要求m <= n。其實偶數時 j 表示成奇數時的式子也是可以的,因為C語言里邊的除法是整除,所以加上1不會影響j的結果。所以j = (m + n + 1) / 2 - i且m <= n。
另一個要求是:

  •   A[i - 1] <= B[j]
  •   B[j - 1] <= A[i]

  令i = (begin + end) / 2:

  •   如果A[i - 1] > B[j],則說明需要減小i,減小i的同時j也會增大,這樣A[i - 1]的值就會減小,B[j]的值會增大,向着滿足條件的方向靠近。而且因為從i到end之間A是遞增的,所以i到end之間的都不符合(i越大,A[i - 1]越大,B[j]越小),故直接將end置為i - 1。
  •   如果B[j - 1] > A[i],則說明需要減小j,減小j的意味着增大i,這樣A[i]的值就會增大,B[j + 1]的值會減小,向着滿足條件的方向靠近。而且因為從begin到i之間A是遞增的,所以begin到i之間的都不符合(i越小,A[i - 1]越小,B[j]越大),故直接將begin置為i + 1。
  •   如果兩個條件都滿足說明已經遍歷到正確的中間位置,進行后續邏輯判斷即可。需要注意的是邊界情況,在判斷時一定要保證數組索引在范圍之內,對於不符合的情況,進入到最終的判斷邏輯中進行處理。運行時間24ms,代碼如下。

 

 1 #define min(a, b) (a < b ? a : b)
 2 #define max(a, b) (a > b ? a : b)
 3 double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
 4     int m = nums1Size, n = nums2Size;
 5     int t = 0;
 6     if(m > n) {
 7         int* temp = nums1;
 8         nums1 = nums2;
 9         nums2 = temp;
10         t = m;
11         m = n;
12         n = t;
13     }
14     int i = 0, j = 0, left = 0, right = 0;
15     int begin = 0, end = m;
16     t = (m + n + 1) / 2;
17     while(begin <= end) {
18         i = (begin + end) / 2;
19         j = t - i;
20         if(i > 0 && j < n && nums1[i - 1] > nums2[j])
21             end = i - 1;
22         else if(j > 0 && i < m && nums2[j - 1] > nums1[i])
23             begin = i + 1;
24         else {
25             if(i == 0)
26                 left = nums2[j - 1];  
27             else if(j == 0)
28                 left = nums1[i - 1];
29             else
30                 left = max(nums1[i - 1], nums2[j - 1]);
31             if(i == m)
32                 right = nums2[j];
33             else if(j == n)
34                 right = nums1[i];
35             else 
36                 right = min(nums1[i], nums2[j]);
37             if((m + n) & 1)
38                 return left;
39             else
40                 return (left + right) / 2.0;
41         } 
42     }
43     return 0;
44 }

原文地址:

https://blog.csdn.net/hang404/article/details/84786904


免責聲明!

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



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