leetcode之尋找兩個正序數組的中位數

問題描述:

給定兩個大小爲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

解法一:

簡單粗暴,將兩個數組合並,兩個有序數組的合併也是歸併排序的一部分,然後根據奇數還是偶數,返回中位數。

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
     //簡單粗暴的方法,將兩個有序數組合並,再求中位數
		int len_nums1 =nums1.length;
		int len_nums2 =nums2.length;
		int[] nums = new int[len_nums1 + len_nums2];
		//當兩個數組中其中一個爲空的情況
		if(len_nums1 == 0){
			if(len_nums2 % 2 ==0 ){
				return (nums2[len_nums2/2]+nums2[len_nums2/2-1])/2.0;
			}else{
				return nums2[(len_nums2-1)/2];
			}
		}
		if(len_nums2 == 0){
			if(len_nums1 % 2 ==0 ){
				return (nums2[len_nums1/2]+nums2[len_nums1/2+1])/2.0;
			}else{
				return nums2[(len_nums1+1)/2];
			}
		}
		//當兩個數組都不爲空的情況
		int i = 0;
		int j = 0;
		int count = 0;
		while(count < (len_nums1 + len_nums2)){
            if(i == len_nums1){
				while(j < len_nums2){ //數組nums2被訪問完,數組nums2沒訪問完
					nums[count++] =nums2[j++];
				}
                break;
			}
			if(j == len_nums2){
				while(i < len_nums1){ //數組nums2被訪問完,數組nums2沒訪問完
					nums[count++] =nums1[i++];
				}
                break;
			}
			if(nums1[i]<nums2[j]){//歸併排序的思想
				nums[count++] = nums1[i++];
			}else{
				nums[count++] = nums2[j++];
			}
		}
		if((len_nums1 + len_nums2) % 2 == 0){
			return (nums[count/2] +nums[count/2- 1])/2.0;
		}else{
			return nums[count/2];
		}
    }
}

時間複雜度:遍歷全部數組 (m+n)

空間複雜度:開闢了一個數組,保存合併後的兩個數組 O(m+n)

解法二:

其實,我們不需要將兩個數組真的合併,我們只需要找到中位數在哪裏就可以了。

開始的思路是寫一個循環,然後裏邊判斷是否到了中位數的位置,到了就返回結果,但這裏對偶數和奇數的分類會很麻煩。當其中一個數組遍歷完後,出了 for 循環對邊界的判斷也會分幾種情況。總體來說,雖然複雜度不影響,但代碼會看起來很亂。

首先是怎麼將奇數和偶數的情況合併一下。

用 len 表示合併後數組的長度,如果是奇數,我們需要知道第 (len+1)/2 個數就可以了,如果遍歷的話需要遍歷 int(len/2 ) + 1 次。如果是偶數,我們需要知道第 len/2和 len/2+1 個數,也是需要遍歷 len/2+1 次。所以遍歷的話,奇數和偶數都是 len/2+1 次。

返回中位數的話,奇數需要最後一次遍歷的結果就可以了,偶數需要最後一次和上一次遍歷的結果。所以我們用兩個變量 left 和 right,right 保存當前循環的結果,在每次循環前將 right 的值賦給 left。這樣在最後一次循環的時候,left 將得到 right 的值,也就是上一次循環的結果,接下來 right 更新爲最後一次的結果。

循環中該怎麼寫,什麼時候 A 數組後移,什麼時候 B 數組後移。用 aStart 和 bStart 分別表示當前指向 A 數組和 B 數組的位置。如果 aStart 還沒有到最後並且此時 A 位置的數字小於 B 位置的數組,那麼就可以後移了。也就是aStart<m&&A[aStart]< B[bStart]。

但如果 B 數組此刻已經沒有數字了,繼續取數字 B[ bStart ],則會越界,所以判斷下 bStart 是否大於數組長度了,這樣 || 後邊的就不會執行了,也就不會導致錯誤了,所以增加爲 aStart<m&&(bStart) >= n||A[aStart]<B[bStart]) 。

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        int len = m + n;
        int[] nums = new int[len];
        
        int left = 0;
        int right = 0;
        int index_s1 = 0;
        int index_s2 = 0;
         
        for(int k = 0; k <= len/2;k++){
            left = right;
            if(index_s1 < m &&(index_s2 >= n || nums1[index_s1] < nums2[index_s2])){
                right = nums1[index_s1++];
            }else{
                right = nums2[index_s2++];
            }
        }
        if(len % 2 == 0){
            return (left+right)/2.0;
        }else{
            return right;
        }
    }
}

時間複雜度:遍歷 len/2+1 次,len=m+n,所以時間複雜度依舊是 O(m+n)

空間複雜度:我們申請了常數個變量,也就是 m,n,len,left,right,aStart,bStart 以及 i。總共 8 個變量,所以空間複雜度是 O(1)

 

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