LeetCode 4 兩個排序數組的中位數 / Median of Two Sorted Arrays

題目描述:

  這題還是有點難度的,想了比較久,最開始的時候沒看見複雜度要求,心想不就是把兩個數組合並起來就可以了麼,爲什麼tag是困難,結果看見複雜度要求,才知道要二分做,一開始二分又想錯了方向,嘗試建立兩個數組各自的中位數的聯繫,結果寫了一大堆發現自己走錯了路,最後參考別人的思路才做出。

解題思路:本題核心函數就一個: double findKth(vector<int> &nums1, int i, vector<int> &nums2, int j, int k)

表示找到兩個排序數組最後合併排序後的第k個數,其中 int i和int j表示還沒搜索過的元素的起點(比如初始的時候i和j都等於0)

那麼怎麼在沒有合併的情況下,怎麼找到第k個數呢?

思路是這樣的:判斷 nums1 數組中第k/2個數 和 nums2 數組中第k/2個數的大小關係,比如 nums1[k/2-1] < nums2[k/2-1],那麼第k個數肯定不在nums1[0] 到 nums1[k/2-1]這個區間內,因此,直接把nums1[0] 到 nums1[k/2-1]這個區間排除,在對剩下元素的遞歸處理。這樣說可能有點抽象,舉個具體的例子來說明下。

比如:nums1 裏面有 10 個元素,nums2裏面也有 10 個元素,現在問題要求第16個元素是多少( k = 16)?

首先,k/2 = 8 ,找到nums1中第8個元素nums1[7],和nums2中第k - k/2 = 8個元素nums2[7],如果nums1[7]<nums2[7],那麼答案肯定不在nums1[0]到num1[7]之間,因爲即使最壞的情況,nums2[0]到nums2[6]全在nums1[0]到nums1[7]裏面,那麼這裏面一共也才15個元素 < (k = 16),所以直接把nums1[0]到nums1[7] 排除掉,對剩下的elems遞歸處理。

本題的核心算法就是上面一段,還有個難點就是關於座標的換算,我主要就是卡在了這個地方,具體看以下代碼,可以結合上一段舉的例子來理解:

class Solution {
	public:
		/**
		 * @param A: An integer array.
		 * @param B: An integer array.
		 * @return: a double whose format is *.5 or *.0
		 */
		double findMedianSortedArrays(vector<int> A, vector<int> B) {
			// write your code here
			int sizeA = A.size(), sizeB = B.size();
			if (sizeA <= 0 && sizeB <= 0) {
				return 0;
			}

			int total = sizeA + sizeB;
			if (total % 2 == 1) {
				return findKth(A, 0, B, 0, total / 2 + 1);
			}
			else {//偶數則取中間兩個值的平均數
				return (findKth(A, 0, B, 0, total / 2) + findKth(A, 0, B, 0, total / 2 + 1)) / 2;
			}
		}
		
		double findKth(vector<int> &nums1, int i, vector<int> &nums2, int j, int k) {
			//一定要保證nums1的長度小於nums2的長度,不然對nums2的訪問可能會越界
			if (nums1.size() - i > nums2.size() - j) {
				return findKth(nums2, j, nums1, i, k);
			}
			// 判斷小的數組是否爲空,爲空的話,直接在另一個數組找第K個即可
			if (nums1.size() == i) {//遞歸結束情況1
				return nums2[j + k - 1];
			}
			// 當K = 1時,表示我們要找第一個元素,只要比較兩個數組的第一個元素,返回較小的那個即可
			if (k == 1) {//遞歸結束情況2
				return min(nums1[i], nums2[j]);
			}
            //pa表示在nums1中定位的元素的座標加上一(下面訪問的時候會減掉),pb同理
			int pa = min(i + k / 2, int(nums1.size())), pb = j + k - (pa - i);//nums1中的元素不夠k/2的話,那麼就有多少取多少(nums1.size())                                                  //pa - i :在nums1中已取得元素的個數                                                                          //k - (pa - i):剩下的還需要在nums2中取得元素的個數
			
			if (nums1[pa - 1] < nums2[pb - 1]) {
				return findKth(nums1, pa, nums2, j, k - pa + i);
			}
			else if (nums1[pa - 1] > nums2[pb - 1]) {
				return findKth(nums1, i, nums2, pb, k - pb + j);
			}
			else {
				return nums1[pa - 1];//相等的話說明說明這就是答案,直接返回即可
			}
		}
	};
還有爲什麼要二分呢,其實你nums1中取k/3個,nums2中取2k/3個,然後比較nums1[k/3-1]和nums2[2k/3-1]也可以,因爲明顯二分最快。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章