寻找两个有序数组的中位数
给定两个大小为 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(object):
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
nums1.extend(nums2)
nums1 = sorted(nums1)
len_num = len(nums1)
if len_num % 2 == 0:
tmp = int(len_num / 2)
re = (float)(nums1[tmp-1] + nums1[tmp])
return re/2
else:
tmp = int((len_num-1) / 2)
return nums1[tmp]
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
三种取值的排序
一个数组里面只有’r’,‘g’,'b’三种字符随机排序,如果用In-place的方法让这个数组排好顺序,排序后的样子是[r,r,r,g,g,g,b,b,b]
解法
- 暴力:用哈希法存下每种元素的个数,最后直接赋值
- 三个指针:明确在 p0 左侧维护 r, p1 右侧维护 g, p2 右侧维护 b, 然后使用 p0 朝右扫描,如果扫到 g 很容易处理,如果扫到 b 就需要把 p1, p2 各自移动一位,并在移动过程中注意维护各个区间中元素成分是否正确
def rgbSort(arr):
n = len(arr)
p1 = p2 = n - 1
p0 = 0
while p0 <= p1:
if arr[p0] == 'g':
arr[p0], arr[p1] = arr[p1], arr[p0]
p1 -= 1
elif arr[p0] == 'b':
arr[p0], arr[p1] = arr[p1], arr[p0]
arr[p1], arr[p2] = arr[p2