当前位置: 代码迷 >> 综合 >> leetcode | Median of Two Sorted Arrays 寻找2个有序数组中第k大的值
  详细解决方案

leetcode | Median of Two Sorted Arrays 寻找2个有序数组中第k大的值

热度:3   发布时间:2023-12-13 19:37:59.0

问题 Median of Two Sorted Arrays

There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays. 
The overall run time complexity should be O(log(m + n)).

分析

本题更经典通用的描述方式时:
给定2个有序数组,找出2个数组中所有元素中第k大的元素。

思路1

直观思路就是类似merge-sort,将2个数组merge成一个数组,即可得到第k大的值,但是时间复杂度 O(m+n) ;

思路2

然后我们仅需要第k大的元素,不需要排序这个复杂的操作:可以定义一个计数器m,表示找到了第m大的元素;同时指针pa,pb分别指向数组A,B的第一个元素,使用merge-sort的方式,当A的当前元素小于B的当前元素时:pa++, m++,当*pb < *pa时,pb++, m++。最终当m等于k时,就得到了第k大的元素。时间复杂度 O(k) ,但是当k接近于m+n时,复杂度还是 O(m+n) ;

思路3

从题目中的要求 O(log(m+n)) 可以联想到肯定要用到二分查找的思想

那么有没有更好的方案?我们可以考虑从k入手。如果我们每次能够删除一个一定处于第k大元素之前的元素,那么需要进行k次。但是如果我们每次都能删除一半呢?可以利用A,B有序的信息,类似二分查找,也是充分利用有序。
假设A 和B 的元素个数都大于k/2,我们将A 的第k/2 个元素(即A[k/2-1])和B 的第k/2个元素(即B[k/2-1])进行比较,有以下三种情况(为了简化这里先假设k 为偶数,所得到的结论对于k 是奇数也是成立的):
- A[k/2 - 1] == B[k/2 - 1];
- A[k/2 - 1] > B[k/2 - 1];
- A[k/2 - 1] < B[k/2 - 1];
如果A[k/2 - 1] < B[k/2 - 1] ,意味着 A[0] 到 A[k/2 - 1] 的元素一定小于 A+B 第k大的元素。因此可以放心的删除A数组中的这k/2个元素;
同理,A[k/2 - 1] > B[k/2 - 1];可以删除B数组中的k/2个元素;
当A[k/2 - 1] == B[k/2 - 1] 时,说明找到了第k大的元素,直接返回A[k/2 - 1] 或B[k/2 - 1]的值。

因此可以写一个递归实现,递归终止条件是什么呢?
- A或B为空时,直接返回A[k-1] 或 B[k-1]
- 当k = 1时,返回min(A[0], B[0]) //第1小表示第一个元素
- 当A[k/2 - 1] == B[k/2 - 1] 时,返回A[k/2 - 1] 或B[k/2 - 1]

C语言实现

    static int find_kth(int* A, int m,int* B, int n, int k);static int min(p, q) {
   return (p < q) ? p : q;}double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {int m = nums1Size;int n = nums2Size;int total = m+n;int k = total/2;if(total & 0x01) {return find_kth(nums1, m, nums2, n, k+1); //奇数,返回唯一中间值} else {return (find_kth(nums1, m, nums2, n, k) + find_kth(nums1, m, nums2, n, k+1)) / 2.0; //偶数,返回中间2个的平均值}}//找到A,B组合中第k小的值: AB[k-1]int find_kth(int* A, int m,int* B, int n, int k) {//假设m都小于nif (m > n)return find_kth(B, n, A, m, k);if (m == 0)return B[k-1];if (k == 1) //终止条件return min(A[0], B[0]);int i_a = min(m, k/2);int i_b = k - i_a;if (A[i_a-1] < B[i_b-1])return find_kth(A+i_a, m-i_a, B, n, k-i_a);else if (A[i_a-1] > B[i_b-1])return find_kth(A, m, B+i_b, n-i_b, k-i_b);elsereturn A[i_a-1];}

参考:https://github.com/soulmachine/leetcode

  相关解决方案