精品伊人久久大香线蕉,开心久久婷婷综合中文字幕,杏田冲梨,人妻无码aⅴ不卡中文字幕

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
【小Y學算法】??每日LeetCode打卡??——4.尋找兩個正序數組的中位數


📢前言

  • 🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻
  • 🌲 每天打卡一道算法題,既是一個學習過程,又是一個分享的過程😜
  • 🌲 提示:本專欄解題 編程語言一律使用 C# 和 Java 兩種進行解題
  • 🌲 要保持一個每天都在學習的狀態,讓我們一起努力成為算法大神吧🧐!
  • 🌲 今天是力扣算法題持續打卡第4天🎈!
  • 🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻🌻

🌲原題樣例

給定兩個大小分別為 m 和 n 的正序(從小到大)數組 nums1 和 nums2。請你找出并返回這兩個正序數組的 中位數 。

示例 1:

輸入:nums1 = [1,3], nums2 = [2]
輸出:2.00000
解釋:合并數組 = [1,2,3] ,中位數 2

示例 2:

輸入:nums1 = [1,2], nums2 = [3,4]
輸出:2.50000
解釋:合并數組 = [1,2,3,4] ,中位數 (2 + 3) / 2 = 2.5

示例 3:

輸入:nums1 = [0,0], nums2 = [0,0]
輸出:0.00000

示例 4:

輸入:nums1 = [], nums2 = [1]
輸出:1.00000
示例 5:

輸入:nums1 = [2], nums2 = []
輸出:2.00000

提示:

  • nums1.length == m

  • nums2.length == n

  • 0 <= m <= 1000

  • 0 <= n <= 1000

  • 1 <= m + n <= 2000

  • -106 <= nums1[i], nums2[i] <= 106


🌻C#方法一:合并List根據長度找中位數

解題思路

new 一個 List , 并將 nums1 和 nums2 都添加到list 中,然后進行排序。對于排序后的 list, 根據長度計算出中位數的index,進而計算出最終結果。

假設合并后的list長度為13,則從小到大第7個數字為中位數,resultIndex=6;

假設合并后的list長度為14,則從小到大第7,8個數字的平均值為中位數,index 分別為 6,7,此時resultIndex =7,resultIndex-1 =6 , 結果為 ( list[resultIndex-1] + list[resultIndex] ) / 2.0 ;

public class Solution {
     public double FindMedianSortedArrays(int[] nums1, int[] nums2)
    {
        int m = nums1.Length;
        int n = nums2.Length;
        int len = m + n;
        var resultIndex = len / 2;
        List<int> list = new List<int>(nums1);
        list.AddRange(nums2);
        list.Sort();
        if (len % 2 == 0)
        {
            return (list[resultIndex - 1] + list[resultIndex]) / 2.0;
        }
        else
        {
            return list[resultIndex];
        }
    }

}

執行結果

執行通過,執行用時108ms,內存消耗28 MB.

復雜度分析
時間復雜度:O( (m+n)(1+log(m+n) ))
將長度為m,n的兩個數組添加到list,復雜度分別為常數級的m和n ;list.Sort()的復雜度根據官方文檔可得為 (m+n)log(m+n),所以該方法時間復雜度為 O( m+n+(m+n)log(m+n) ) = O( (m+n)(1+log(m+n) ))

空間復雜度:O(m+n)
使用list的長度為m+n.

🌻C#方法一:歸并排序后根據長度找中位數

方法一使用了list.Sort() 方法,可以對list進行排序,但是,若題目給出的nums1 和 nums2 是無序數組,使用 list.Sort() 才算是 物有所用。 本題中 nums1 和 nums2 是有序數組,所以使用 list.Sort() 有寫 殺雞用宰牛刀的感覺,換句話說,這里面存在著效率的浪費。我們可以利用 【nums1 和 nums2 是有序數組】 這個條件,來精簡我們的排序。

public class Solution {
    public double FindMedianSortedArrays(int[] nums1, int[] nums2)
    {
        // nums1 與 nums2 有序添加到list中
        List<int> list = new List<int>();
        int i = 0, j = 0;
        int m = nums1.Length;
        int n = nums2.Length;
        int len = m + n;
        var resultIndex = len / 2;

        while (i < m && j < n)
        {
            if (nums1[i] < nums2[j])
                list.Add(nums1[i++]);
            else
                list.Add(nums2[j++]);
        }
        while (i < m) list.Add(nums1[i++]);
        while (j < n) list.Add(nums2[j++]);

        if (len % 2 == 0)
        {
            return (list[resultIndex - 1] + list[resultIndex]) / 2.0;
        }
        else
        {
            return list[resultIndex];
        }
    }

}

執行結果
執行結果 通過,執行用時 112ms,內存消耗 28.1MB

復雜度分析
時間復雜度:O(m+n)
i 和 j 一起把長度為 m 和 n 的兩個數組遍歷了一遍,所以時間復雜度為 O(m+n)

空間復雜度:O(m+n)
使用list的長度為m+n.


🌻Java方法一:二分查找(官方解法)

解題思路
給定兩個有序數組,要求找到兩個有序數組的中位數,最直觀的思路有以下兩種:

  • 使用歸并的方式,合并兩個有序數組,得到一個大的有序數組。大的有序數組的中間位置的元素,即為中位數。

  • 不需要合并兩個有序數組,只要找到中位數的位置即可。由于兩個數組的長度已知,因此中位數對應的兩個數組的下標之和也是已知的。維護兩個指針,初始時分別指向兩個數組的下標 00 的位置,每次將指向較小值的指針后移一位(如果一個指針已經到達數組末尾,則只需要移動另一個數組的指針),直到到達中位數的位置。

假設兩個有序數組的長度分別為 mm 和 nn,上述兩種思路的復雜度如何?

第一種思路的時間復雜度是 O(m+n)O(m+n),空間復雜度是 O(m+n)O(m+n)。第二種思路雖然可以將空間復雜度降到 O(1)O(1),但是時間復雜度仍是 O(m+n)O(m+n)。

如何把時間復雜度降低到 O(\log(m+n))O(log(m+n)) 呢?如果對時間復雜度的要求有 \loglog,通常都需要用到二分查找,這道題也可以通過二分查找實現。

根據中位數的定義,當 m+nm+n 是奇數時,中位數是兩個有序數組中的第 (m+n)/2(m+n)/2 個元素,當 m+nm+n 是偶數時,中位數是兩個有序數組中的第 (m+n)/2(m+n)/2 個元素和第 (m+n)/2+1(m+n)/2+1 個元素的平均值。因此,這道題可以轉化成尋找兩個有序數組中的第 kk 小的數,其中 kk 為 (m+n)/2(m+n)/2 或 (m+n)/2+1(m+n)/2+1。

假設兩個有序數組分別是 \text{A}A 和 \text{B}B。要找到第 kk 個元素,我們可以比較 \text{A}[k/2-1]A[k/2?1] 和 \text{B}[k/2-1]B[k/2?1],其中 // 表示整數除法。由于 \text{A}[k/2-1]A[k/2?1] 和 \text{B}[k/2-1]B[k/2?1] 的前面分別有 \text{A}[0,…,k/2-2]A[0…k/2?2] 和 \text{B}[0,…,k/2-2]B[0…k/2?2],即 k/2-1k/2?1 個元素,對于 \text{A}[k/2-1]A[k/2?1] 和 \text{B}[k/2-1]B[k/2?1] 中的較小值,最多只會有 (k/2-1)+(k/2-1) \leq k-2(k/2?1)+(k/2?1)≤k?2 個元素比它小,那么它就不能是第 kk 小的數了。

因此我們可以歸納出三種情況:

  • 如果 \text{A}[k/2-1] < \text{B}[k/2-1]A[k/2?1]<B[k/2?1],則比 \text{A}[k/2-1]A[k/2?1] 小的數最多只有 \text{A}A 的前 k/2-1k/2?1 個數和 \text{B}B 的前 k/2-1k/2?1 個數,即比 \text{A}[k/2-1]A[k/2?1] 小的數最多只有 k-2k?2 個,因此 \text{A}[k/2-1]A[k/2?1] 不可能是第 kk 個數,\text{A}[0]A[0] 到 \text{A}[k/2-1]A[k/2?1] 也都不可能是第 kk 個數,可以全部排除。

  • 如果 \text{A}[k/2-1] > \text{B}[k/2-1]A[k/2?1]>B[k/2?1],則可以排除 \text{B}[0]B[0] 到 \text{B}[k/2-1]B[k/2?1]。

  • 如果 \text{A}[k/2-1] = \text{B}[k/2-1]A[k/2?1]=B[k/2?1],則可以歸入第一種情況處理。

可以看到,比較 \text{A}[k/2-1]A[k/2?1] 和 \text{B}[k/2-1]B[k/2?1] 之后,可以排除 k/2k/2 個不可能是第 kk 小的數,查找范圍縮小了一半。同時,我們將在排除后的新數組上繼續進行二分查找,并且根據我們排除數的個數,減少 kk 的值,這是因為我們排除的數都不大于第 kk 小的數。

有以下三種情況需要特殊處理:

  • 如果 \text{A}[k/2-1]A[k/2?1] 或者 \text{B}[k/2-1]B[k/2?1] 越界,那么我們可以選取對應數組中的最后一個元素。在這種情況下,我們必須根據排除數的個數減少 kk 的值,而不能直接將 kk 減去 k/2k/2。

  • 如果一個數組為空,說明該數組中的所有元素都被排除,我們可以直接返回另一個數組中第 kk 小的元素。

  • 如果 k=1k=1,我們只要返回兩個數組首元素的最小值即可。

用一個例子說明上述算法。假設兩個有序數組如下:

A: 1 3 4 9
B: 1 2 3 4 5 6 7 8 9

兩個有序數組的長度分別是 44 和 99,長度之和是 1313,中位數是兩個有序數組中的第 77 個元素,因此需要找到第 k=7k=7 個元素。

比較兩個有序數組中下標為 k/2-1=2k/2?1=2 的數,即 \text{A}[2]A[2] 和 \text{B}[2]B[2],如下面所示:

A: 1 3 4 9

B: 1 2 3 4 5 6 7 8 9

由于 \text{A}[2] > \text{B}[2]A[2]>B[2],因此排除 \text{B}[0]B[0] 到 \text{B}[2]B[2],即數組 \text{B}B 的下標偏移(offset)變為 33,同時更新 kk 的值:k=k-k/2=4k=k?k/2=4。

下一步尋找,比較兩個有序數組中下標為 k/2-1=1k/2?1=1 的數,即 \text{A}[1]A[1] 和 \text{B}[4]B[4],如下面所示,其中方括號部分表示已經被排除的數。

A: 1 3 4 9

B: [1 2 3] 4 5 6 7 8 9

由于 \text{A}[1] < \text{B}[4]A[1]<B[4],因此排除 \text{A}[0]A[0] 到 \text{A}[1]A[1],即數組 \text{A}A 的下標偏移變為 22,同時更新 kk 的值:k=k-k/2=2k=k?k/2=2。

下一步尋找,比較兩個有序數組中下標為 k/2-1=0k/2?1=0 的數,即比較 \text{A}[2]A[2] 和 \text{B}[3]B[3],如下面所示,其中方括號部分表示已經被排除的數。

A: [1 3] 4 9

B: [1 2 3] 4 5 6 7 8 9

由于 \text{A}[2]=\text{B}[3]A[2]=B[3],根據之前的規則,排除 \text{A}A 中的元素,因此排除 \text{A}[2]A[2],即數組 \text{A}A 的下標偏移變為 33,同時更新 kk 的值: k=k-k/2=1k=k?k/2=1。

由于 kk 的值變成 11,因此比較兩個有序數組中的未排除下標范圍內的第一個數,其中較小的數即為第 kk 個數,由于 \text{A}[3] > \text{B}[3]A[3]>B[3],因此第 kk 個數是 \text{B}[3]=4B[3]=4。

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int length1 = nums1.length, length2 = nums2.length;
        int totalLength = length1 + length2;
        if (totalLength % 2 == 1) {
            int midIndex = totalLength / 2;
            double median = getKthElement(nums1, nums2, midIndex + 1);
            return median;
        } else {
            int midIndex1 = totalLength / 2 - 1, midIndex2 = totalLength / 2;
            double median = (getKthElement(nums1, nums2, midIndex1 + 1) + getKthElement(nums1, nums2, midIndex2 + 1)) / 2.0;
            return median;
        }
    }

    public int getKthElement(int[] nums1, int[] nums2, int k) {
        /* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 進行比較
         * 這里的 "/" 表示整除
         * nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共計 k/2-1 個
         * nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共計 k/2-1 個
         * 取 pivot = min(pivot1, pivot2),兩個數組中小于等于 pivot 的元素共計不會超過 (k/2-1) + (k/2-1) <= k-2 個
         * 這樣 pivot 本身最大也只能是第 k-1 小的元素
         * 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把這些元素全部 "刪除",剩下的作為新的 nums1 數組
         * 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把這些元素全部 "刪除",剩下的作為新的 nums2 數組
         * 由于我們 "刪除" 了一些元素(這些元素都比第 k 小的元素要小),因此需要修改 k 的值,減去刪除的數的個數
         */

        int length1 = nums1.length, length2 = nums2.length;
        int index1 = 0, index2 = 0;
        int kthElement = 0;

        while (true) {
            // 邊界情況
            if (index1 == length1) {
                return nums2[index2 + k - 1];
            }
            if (index2 == length2) {
                return nums1[index1 + k - 1];
            }
            if (k == 1) {
                return Math.min(nums1[index1], nums2[index2]);
            }
            
            // 正常情況
            int half = k / 2;
            int newIndex1 = Math.min(index1 + half, length1) - 1;
            int newIndex2 = Math.min(index2 + half, length2) - 1;
            int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2];
            if (pivot1 <= pivot2) {
                k -= (newIndex1 - index1 + 1);
                index1 = newIndex1 + 1;
            } else {
                k -= (newIndex2 - index2 + 1);
                index2 = newIndex2 + 1;
            }
        }
    }
}

鏈接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-s-114/

執行結果
執行結果 通過,執行用時 2ms,內存消耗 39.9MB

復雜度分析

時間復雜度:O(\log(m+n))O(log(m+n))
空間復雜度:O(1)


🌻Java方法二:第k小數

上邊的兩種思路,時間復雜度都達不到題目的要求 O(log(m+n)O(log(m+n)。看到 log,很明顯,我們只有用到二分的方法才能達到。我們不妨用另一種思路,題目是求中位數,其實就是求第 k 小數的一種特殊情況,而求第 k 小數有一種算法。

解法二中,我們一次遍歷就相當于去掉不可能是中位數的一個值,也就是一個一個排除。由于數列是有序的,其實我們完全可以一半兒一半兒的排除。假設我們要找第 k 小數,我們可以每次循環排除掉 k/2 個數。看下邊一個例子。

假設我們要找第 7 小的數字。

我們比較兩個數組的第 k/2 個數字,如果 k 是奇數,向下取整。也就是比較第 33 個數字,上邊數組中的 44 和下邊數組中的 33,如果哪個小,就表明該數組的前 k/2 個數字都不是第 k 小數字,所以可以排除。也就是 11,22,33 這三個數字不可能是第 77 小的數字,我們可以把它排除掉。將 13491349 和 4567891045678910 兩個數組作為新的數組進行比較。

更一般的情況 A[1] ,A[2] ,A[3],A[k/2] … ,B[1],B[2],B[3],B[k/2] … ,如果 A[k/2]<B[k/2] ,那么A[1],A[2],A[3],A[k/2]都不可能是第 k 小的數字。

A 數組中比 A[k/2] 小的數有 k/2-1 個,B 數組中,B[k/2] 比 A[k/2] 小,假設 B[k/2] 前邊的數字都比 A[k/2] 小,也只有 k/2-1 個,所以比 A[k/2] 小的數字最多有 k/1-1+k/2-1=k-2個,所以 A[k/2] 最多是第 k-1 小的數。而比 A[k/2] 小的數更不可能是第 k 小的數了,所以可以把它們排除。

橙色的部分表示已經去掉的數字。

由于我們已經排除掉了 3 個數字,就是這 3 個數字一定在最前邊,所以在兩個新數組中,我們只需要找第 7 - 3 = 4 小的數字就可以了,也就是 k = 4。此時兩個數組,比較第 2 個數字,3 < 5,所以我們可以把小的那個數組中的 1 ,3 排除掉了。

我們又排除掉 2 個數字,所以現在找第 4 - 2 = 2 小的數字就可以了。此時比較兩個數組中的第 k / 2 = 1 個數,4 == 4,怎么辦呢?由于兩個數相等,所以我們無論去掉哪個數組中的都行,因為去掉 1 個總會保留 1 個的,所以沒有影響。為了統一,我們就假設 4 > 4 吧,所以此時將下邊的 4 去掉。

由于又去掉 1 個數字,此時我們要找第 1 小的數字,所以只需判斷兩個數組中第一個數字哪個小就可以了,也就是 4。

所以第 7 小的數字是 4。

我們每次都是取 k/2 的數進行比較,有時候可能會遇到數組長度小于 k/2的時候。

此時 k / 2 等于 3,而上邊的數組長度是 2,我們此時將箭頭指向它的末尾就可以了。這樣的話,由于 2 < 3,所以就會導致上邊的數組 1,2 都被排除。造成下邊的情況。

由于 2 個元素被排除,所以此時 k = 5,又由于上邊的數組已經空了,我們只需要返回下邊的數組的第 5 個數字就可以了。

從上邊可以看到,無論是找第奇數個還是第偶數個數字,對我們的算法并沒有影響,而且在算法進行中,k 的值都有可能從奇數變為偶數,最終都會變為 1 或者由于一個數組空了,直接返回結果。

所以我們采用遞歸的思路,為了防止數組長度小于 k/2,所以每次比較 min(k/2,len(數組) 對應的數字,把小的那個對應的數組的數字排除,將兩個新數組進入遞歸,并且 k 要減去排除的數字的個數。遞歸出口就是當 k=1 或者其中一個數字長度是 0 了。

代碼

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int n = nums1.length;
    int m = nums2.length;
    int left = (n + m + 1) / 2;
    int right = (n + m + 2) / 2;
    //將偶數和奇數的情況合并,如果是奇數,會求兩次同樣的 k 。
    return (getKth(nums1, 0, n - 1, nums2, 0, m - 1, left) + getKth(nums1, 0, n - 1, nums2, 0, m - 1, right)) * 0.5;  
}
    
    private int getKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k) {
        int len1 = end1 - start1 + 1;
        int len2 = end2 - start2 + 1;
        //讓 len1 的長度小于 len2,這樣就能保證如果有數組空了,一定是 len1 
        if (len1 > len2) return getKth(nums2, start2, end2, nums1, start1, end1, k);
        if (len1 == 0) return nums2[start2 + k - 1];

        if (k == 1) return Math.min(nums1[start1], nums2[start2]);

        int i = start1 + Math.min(len1, k / 2) - 1;
        int j = start2 + Math.min(len2, k / 2) - 1;

        if (nums1[i] > nums2[j]) {
            return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
        }
        else {
            return getKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1));
        }
    }
鏈接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-2/

執行結果
執行結果 通過,執行用時 2ms,內存消耗 39.8MB

復雜度分析

時間復雜度: O(log(m+n)O(log(m+n)
空間復雜度:O(1)


💬總結

  • 今天是力扣算法題打卡的第四天!今天的題有點難,借助力扣大神題解看了半天!
  • 文章采用 C# 和 Java 兩種編程語言進行解題
  • 一些方法也是參考力扣大神寫的,也是邊學習邊分享,再次感謝算法大佬們
  • 那今天的算法題分享到此結束啦,明天再見!

本站僅提供存儲服務,所有內容均由用戶發布,如發現有害或侵權內容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
LeetCode
LeetCode實戰:尋找兩個有序數組的中位數
百度從Google學來的面試題,想進大廠必備!
劍指offer(C++)-JZ56:數組中只出現一次的兩個數字(算法-位運算)
LeetCode題庫1求解兩數之和
665. 非遞減數列
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯系客服!

聯系客服

主站蜘蛛池模板: 竹溪县| 兴化市| 离岛区| 红安县| 清丰县| 威宁| 阳东县| 黑龙江省| 临汾市| 札达县| 玉林市| 沭阳县| 巴东县| 宁城县| 九龙城区| 桂林市| 怀柔区| 黄冈市| 米林县| 高尔夫| 余姚市| 屯昌县| 秦安县| 永春县| 四会市| 贺州市| 屏东市| 漯河市| 高青县| 武城县| 定安县| 德令哈市| 长海县| 汕尾市| 永兴县| 犍为县| 甘洛县| 庆城县| 崇信县| 浪卡子县| 白银市|