leetcode刷题记录
题目 寻找两个正序数组的中位数
题目描述
思路
算法模型:
在两个都有序的数组中找上中位数
题意
O(logN)
题解
分情况
偶数个数的时候, 二分
-
2==2’ 这里的2是表示 位置
-
2>2’ 看谁不会再有机会成为第四小
-
2<2’
[1 2] 和 [3’ 4’]把它理解为是两个等长的有序数组,求它俩整体四个数的第二小,
就是最大的整体的第四小。
递归下去
这4个数不管是谁成为了后续过程中的上中位数,它都是整体的上中位数
奇数个数的时候
-
3==3’
-
3>3’
上面[1, 2] 下面[3’ , 4’, 5’]不等长, 没有办法递归下去
单独验证3’
如果发现自己大于等于2,但又没干过3,你直接返回
如果你发现这个不成立多淘汰掉一个,剩下4’5’跟着上面[1, 2]玩递归 -
3<3’
代码
// A[s1...e1]
// B[s2...e2]
// 一定等长!
// 返回整体的,上中位数!8(4) 10(5) 12(6)
public static int getUpMedian(int[] A, int s1, int e1, int[] B, int s2, int e2) {
int mid1 = 0;
int mid2 = 0;
while (s1 < e1) {
// mid1 = s1 + (e1 - s1) >> 1
mid1 = (s1 + e1) / 2;
mid2 = (s2 + e2) / 2;
if (A[mid1] == B[mid2]) {
return A[mid1];
}
// 两个中点一定不等!
if (((e1 - s1 + 1) & 1) == 1) { // 奇数长度
if (A[mid1] > B[mid2]) {
if (B[mid2] >= A[mid1 - 1]) { //相当于上面说的 3' 没干过 3 但是干过了2 3'就是第 5 小 直接返回
return B[mid2];
}
e1 = mid1 - 1;
s2 = mid2 + 1;
} else { // A[mid1] < B[mid2]
if (A[mid1] >= B[mid2 - 1]) { //同理
return A[mid1];
}
e2 = mid2 - 1;
s1 = mid1 + 1;
}
} else { // 偶数长度
if (A[mid1] > B[mid2]) {
e1 = mid1;
s2 = mid2 + 1;
} else {
e2 = mid2;
s1 = mid1 + 1;
}
}
}
return Math.min(A[s1], B[s2]);
}
进阶:
在两个都有序的数组中找整体第K小的数
进阶问题: 在两个都有序的数组中找整体第K小的数 可以做到O(log(Min(M,N)))
题意
O(log min(M, N))
k的范围1~27
题解
分段讨论
- 1<= k <=短数组长度
两个 等长数组求上中位数
边界也对
- 长数组长度 < k <= 整体长度
手动验证13跟10’, 6’跟17
你可以通过具体的arr1, arr2的长度跟 K 的值换算出哪个部分是不可能的,哪个部分是单独验证的。
边界情况也是对的
- 短数组长度<k <= 长数组长度
短数组全有可能
从5~15是 11 个数,1’~10’是10个数不等长没法用算法原型
手动淘汰5 验5跟10’
为什么手动淘汰5, 而不是15
手动淘汰的目的是一方面是我要接下来调的算法原型要等长,不然的话我不知道他对不对,
或者说我们只实现了等常情况下上中位数的原型,这是第1个目的,第2个目的就是让我淘汰掉的树和出来的数正好能凑够那个位置
复杂度
拿等长两段二分, 所以要拿短数组的长度为主
// 进阶问题 : 在两个都有序的数组中,找整体第K小的数
// 可以做到O(log(Min(M,N)))
public static int findKthNum(int[] arr1, int[] arr2, int kth) {
int[] longs = arr1.length >= arr2.length ? arr1 : arr2;
int[] shorts = arr1.length < arr2.length ? arr1 : arr2;
int l = longs.length;
int s = shorts.length;
if (kth <= s) { // 1)
return getUpMedian(shorts, 0, kth - 1, longs, 0, kth - 1);
}
if (kth > l) { // 3)
if (shorts[kth - l - 1] >= longs[l - 1]) {
return shorts[kth - l - 1];
}
if (longs[kth - s - 1] >= shorts[s - 1]) {
return longs[kth - s - 1];
}
return getUpMedian(shorts, kth - l, s - 1, longs, kth - s, l - 1);
}
// 2) s < k <= l
if (longs[kth - s - 1] >= shorts[s - 1]) { //就是例子中的手动淘汰 5
return longs[kth - s - 1];
}
return getUpMedian(shorts, 0, s - 1, longs, kth - s, kth - 1);
}
LeetCode
4.寻找两个正序数组的中位数 [H]
最多就掉两回 findKthNum 加起来再除二
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int size = nums1.length + nums2.length;
boolean even = (size & 1) == 0;
if (nums1.length != 0 && nums2.length != 0) {
if (even) {
return (double) (findKthNum(nums1, nums2, size / 2) + findKthNum(nums1, nums2, size / 2 + 1)) / 2D;
} else {
return findKthNum(nums1, nums2, size / 2 + 1);
}
} else if (nums1.length != 0) {
if (even) {
return (double) (nums1[(size - 1) / 2] + nums1[size / 2]) / 2;
} else {
return nums1[size / 2];
}
} else if (nums2.length != 0) {
if (even) {
return (double) (nums2[(size - 1) / 2] + nums2[size / 2]) / 2;
} else {
return nums2[size / 2];
}
} else {
return 0;
}
}