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

解法
參考網上的題解:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/shuang-zhi-zhen-by-powcai/
首先,我們理解什麼中位數:指的是該數左右個數相等。
比如:odd : [1,| 2 |,3],2 就是這個數組的中位數,左右兩邊都只要 1 位;
even: [1,| 2, 3 |,4],2,3 就是這個數組的中位數,左右兩邊 1 位;

那麼,現在我們有兩個數組:
num1: [a1,a2,a3,…an]
nums2: [b1,b2,b3,…bn]

[nums1[:left1],nums2[:left2] | nums1[left1:], nums2[left2:]]
只要保證左右兩邊 個數 相同,中位數就在 | 這個邊界旁邊產生。
如何找邊界值,我們可以用二分法,我們先確定 num1 取 m1 個數的左半邊,那麼 num2 取 m2 = (m+n+1)/2 - m1 的左半邊,找到合適的 m1,就用二分法找。
當 [ [a1],[b1,b2,b3] | [a2,…an],[b4,…bn] ]
我們只需要比較 b3 和 a2 的關係的大小,就可以知道這種分法是不是準確的!

  • a2 < b3:說明a2不夠大,nums1應該再往左邊多分一些,即m1應該再加大些
  • a2 >= b3: a2有可能太大了,所以m1可以少一些;也有可能當前的位置正好,所以right = m1

例如:我們令:
nums1 = [-1,1,3,5,7,9]
nums2 =[2,4,6,8,10,12,14,16]
當 m1 = 4,m2 = 3 ,它的中位數就是median = (num1[m1] + num2[m2])/2
時間複雜度:O(log(min(m,n)))O(log(min(m,n)))

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        n1 = len(nums1)
        n2 = len(nums2)
        if n1 > n2:
            return self.findMedianSortedArrays(nums2,nums1)
        k = (n1 + n2 + 1)//2
        left = 0
        right = n1
        while left < right :
            m1 = left +(right - left)//2
            m2 = k - m1
            if nums1[m1] < nums2[m2-1]:
                left = m1 + 1
            else:
                right = m1
        m1 = left
        m2 = k - m1 
        c1 = max(nums1[m1-1] if m1 > 0 else float("-inf"), nums2[m2-1] if m2 > 0 else float("-inf") )
        if (n1 + n2) % 2 == 1:
            return c1
        c2 = min(nums1[m1] if m1 < n1 else float("inf"), nums2[m2] if m2 <n2 else float("inf"))
        return (c1 + c2) / 2

找出第 k 小的距離對

給定一個整數數組,返回所有數對之間的第 k 個最小距離。一對 (A, B) 的距離被定義爲 A 和 B 之間的絕對差值。

示例 1:

輸入:
nums = [1,3,1]
k = 1
輸出:0
解釋:
所有數對如下:
(1,3) -> 2
(1,1) -> 0
(3,1) -> 2
因此第 1 個最小距離的數對是 (1,1),它們之間的距離爲 0。

提示:
2 <= len(nums) <= 10000.
0 <= nums[i] < 1000000.
1 <= k <= len(nums) * (len(nums) - 1) / 2.

解法
參考網上題解:https://leetcode-cn.com/problems/find-k-th-smallest-pair-distance/solution/hei-ming-dan-zhong-de-sui-ji-shu-by-leetcode/

由於第 k 小的距離一定在 [0, W = max(nums) - min(nums)] 中,我們在這個區間上進行二分。對於當前二分的位置 mid,統計距離小於等於 mid 的距離對數量,並根據它和 k 的關係調整區間的上下界。

具體實現的時候,我們可以使用雙指針來計算出所有小於等於 mid 的距離對數目。我們維護 left 和 right,其中 right 通過循環逐漸遞增,left 在每次循環中被維護,使得它滿足 nums[right] - nums[left] <= mid 且最小。
這樣對於 nums[right],以它爲右端的滿足距離小於等於 mid 的距離對數目即爲 right - left。我們在循環中對這些 right - left 進行累加,就得到了所有小於等於 mid 的距離對數目。

class Solution(object):
    def smallestDistancePair(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        
        nums = sorted(nums)
        l, r = 0, nums[-1] - nums[0]
        while l < r:
            mid = (l + r) // 2
            cnt = self.count(nums, mid)
            if cnt < k:
                l = mid + 1
            else:
                r = mid
                
        return l
    
    def count(self, nums, k):
        cnt = 0
        l = 0
        for r, x in enumerate(nums):
            while x - nums[l] > k:
                l += 1
            cnt += r - l
            
        return cnt
        

分割數組的最大值

給定一個非負整數數組和一個整數 m,你需要將這個數組分成 m 個非空的連續子數組。設計一個算法使得這 m 個子數組各自和的最大值最小。

注意:
數組長度 n 滿足以下條件:

1 ≤ n ≤ 1000
1 ≤ m ≤ min(50, n)
示例:

輸入:
nums = [7,2,5,10,8]
m = 2

輸出:
18

解釋:
一共有四種方法將nums分割爲2個子數組。
其中最好的方式是將其分爲[7,2,5] 和 [10,8],
因爲此時這兩個子數組各自的和的最大值爲18,在所有情況中最小。

解法
首先分析題意,可以得出結論,結果必定落在 [max(nums), sum(nums)] 這個區間內,因爲左端點對應每個單獨的元素構成一個子數組,右端點對應所有元素構成一個子數組。
然後可以利用二分查找法每次猜測一個答案,然後模擬一下劃分子數組的過程,可以得到用這個mid值會一共得到的子區間數cnt,然後比較cnt和m的關係,來更新區間範圍。

class Solution(object):
    def splitArray(self, nums, m):
        """
        :type nums: List[int]
        :type m: int
        :rtype: int
        """
        if len(nums) == m:
            return max(nums)
        
        l, r = max(nums), sum(nums)
        while l < r:
            mid = (l + r) // 2
            
            cnt, tmp = 1, 0 
            for x in nums:
                tmp += x
                if tmp > mid:
                    tmp = x
                    cnt += 1
                    
            if cnt > m:
                l = mid + 1
            else:
                r = mid
                
        return l 
        

存在重複元素 III

給定一個整數數組,判斷數組中是否有兩個不同的索引 i 和 j,使得 nums [i] 和 nums [j] 的差的絕對值最大爲 t,並且 i 和 j 之間的差的絕對值最大爲 ķ。

示例 1:

輸入: nums = [1,2,3,1], k = 3, t = 0
輸出: true
示例 2:

輸入: nums = [1,0,1,1], k = 1, t = 2
輸出: true
示例 3:

輸入: nums = [1,5,9,1,5,9], k = 2, t = 3
輸出: false

解法
參考:https://leetcode-cn.com/problems/contains-duplicate-iii/solution/li-yong-tong-de-yuan-li-onpython3-by-zhou-pen-chen/
首先,定義桶的大小是t+1, nums[i]//(t+1)決定放入幾號桶,這樣在一個桶裏面的任意兩個的絕對值差值都<=t
例如t=3, nums=[0 ,5, 1, 9, 3,4],那麼0號桶就有[0,1,3],1號桶就有[4,5],2號桶就有[9]

先不考慮索引差值最大爲K的限制,那麼遍歷nums每一個元素,並把他們放入相應的桶中,有兩種情況會返回True

要放入的桶中已經有其他元素了,這時將nums[i]放進去滿足差值<=t
可能存在前面一個桶的元素並且與nums[i]的差值<=t 或者 存在後面一個桶的元素並且與nums[i]的差值<=t
根據返回True的第一個條件,可以知道前後桶的元素最多也只能有一個。

接着考慮限制桶中的索引差最大爲K,當i>=k的時候:
我們就要去刪除存放着nums[i-k]的那個桶(編號爲nums[i-k]//(t+1))
這樣就能保證遍歷到第i+1個元素時,全部桶中元素的索引最小值是i-k+1,就滿足題目對索引的限制了

class Solution(object):
    def containsNearbyAlmostDuplicate(self, nums, k, t):
        """
        :type nums: List[int]
        :type k: int
        :type t: int
        :rtype: bool
        """
        if k < 0 or t < 0 :
            return False
        dic = {}
        size = t + 1
        for i in range(len(nums)):
            x = nums[i] // size
            if x in dic:
                return True
            
            dic[x] = nums[i]
            if x - 1 in dic and abs(dic[x - 1] - nums[i]) <= t:
                return True
            if x + 1 in dic and abs(dic[x + 1] - nums[i]) <= t:
                return True
            
            if i  >= k:
                dic.pop(nums[i - k] // size)
            
        return False
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章