尋找兩個有序數組的中位數
給定兩個大小爲 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