两个数组第k大的元素
原理:双指针+二分法
如果数组无序,需要先排序。对于已经排好序的数组来说,使用双指针从0位置开始,依次比较每一位就可以找到第k个元素,时间复杂度o(m+n)(m和n为数组长度)。那么如何如何优化呢,二分法可以做到。指针每次移动(k+1)/2位。
过程
- 两个数组指针都赋值为-1
- 从当前位置向后查看第(k+1)/2位,那个数组小就把它的指针向后移动(k+1)/2
- 更新k,k=k-(k+1)/2,重复第二步,直到k=0
- 如果某个数组的指针仍未-1,那么kth大的数就是另一个数组的
- 否则,kth值就是两个数组中较大的那个值
代码
为什么每次向后查(k+1)/2位呢?因为当k=1,k/2=0;(k+1)/2=1.
int getTopK(vector<int> &nums1, vector<int> &nums2, int k) {
//保证nums1较长
if (nums1.size() < nums2.size()) {
return getTopK(nums2, nums1, k);
}
int begin1 = -1;
int begin2 = -1; // 合并后数组前k个在两个数组所选择的数的最后一个元素的下标
int end1 = nums1.size() - 1;
int end2 = nums2.size() - 1;
// 处理两个数组有一个是空数组的情况
if (end2 < 0)
return nums1[k - 1];
while (k > 0) {
// 处理数组越界的情况
if (begin1 + (k + 1) / 2 > end1) {
begin2 += k - (end1 - begin1);
k = (end1 - begin1);
continue;
}
if (begin2 + (k + 1) / 2 > end2) {
begin1 += k - (end2 - begin2);
k = (end2 - begin2);
continue;
}
// 更小的那段一定是属于合并后那前k个数
if (nums1[begin1 + (k + 1) / 2] < nums2[begin2 + (k + 1) / 2])
begin1 += (k + 1) / 2;
else
begin2 += (k + 1) / 2;
k = k - (k + 1) / 2;
}
// 处理某个数组一个都不取的情况
if (begin1 < 0)
return nums2[begin2];
if (begin2 < 0)
return nums1[begin1];
return max(nums1[begin1], nums2[begin2]);
// begin1与begin2是各自的最后一个元素但是最终合并后数组中最大元素是两者更大的那个
}