【21天考研算法带刷leetcode-4】408的一道题源寻找两个有序数组的中位数(2/21)

发布于:2022-12-31 ⋅ 阅读:(515) ⋅ 点赞:(0)

力扣

这道题是408的一道题源,在408中做了简化有了更多的方法,那么力扣上标记困难的题目有没有比较好想以及实现的方法呢?求中位数的本质就是找到中间的数(奇数)或者中线左边+中线右边(偶数)。

这道题目要求返回两个数组合并后的中位数,最简单的思路是需要额外的数组来将两个数组存储起来,然后在使用排序算法,再返回中位数,在这里提供空间复杂度为O(n)两个方法。

转化题目: 找两个数组的第k小的个数,调用两次,先看一下主函数

double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {
	int m = nums1Size; int n = nums2Size;
	if ((m + n) % 2 == 1) {
		return findK(nums1, m, nums2, n, (m + n) / 2 + 1);//(k=1)
	}
	else return (findK(nums1, m, nums2, n, (m + n) / 2) + findK(nums1, m, nums2, n, (m + n) / 2 + 1)) / 2.0;
	//(k=2,k=3)
}

主要来看一下findk的两种实现方式:

一:双指针法

分别在两个数组中进行遍历,nums1[a]>nums2[b],则让a移动并计数,直到计数到k终止,这里要考虑特殊情况,a遍历完但是没结束,则剩下的计数全部让b移动,反之也一样

double findK(int* nums1, int m, int* nums2, int n, int k) {
	int a = 0;
	int b = 0;
	int ans = 0;
	while (k > 0 && a < m && b < n) {
		if (nums1[a] < nums2[b]) {
			ans = nums1[a];
			a++;
			k--;

		}
		else {
			ans = nums2[b];
			b++;
			k--;
		}
	}
	if (k == 0) return ans;
	if (k != 0 && a == m) {
		return nums2[b + k];
	}
	else return nums1[a + k];
}

二:划界法(查找线的过程用到了二分查找)

本质就是保证线左边的两个数组元素个数为k,且左边的元素小于右边的。

 

假设数组一是较小的数组,找到数组一的分界线,实际上也是找到了数组二的分界线(k-i),因为同一个数组内有序,左小于右,要想保证左边都小于右边,否则就会出现下面这种情况

 也就是代码中的right=i-1操作,带着left和right是数组一找分界线的区间,i和j分别代表两个数组的分界线的思想来理解下面代码,每次都是用二分查找假设分解线的位置当停下来的时候left=right=i=数组一的分界线

在代码中添加了许多的注释。

double findK(int* nums1, int m, int* nums2, int n,int k){
    if(m>n){
        return findK(nums2,n, nums1,m,k);//保证第一个是较小的数组
    }
    int totaleft=k;//这道题本质是寻找第k小的数的变种
    int left=0;int right=m>k?k:m//是定义的较小的那个区间(nums1)的两个指针,进行移动,在其中找值
    while(left<right){
        int i=left+(right-left+1)/2;//统一向上取整(可以实现left至少会+1,防止死循环)
        int j=totaleft-i;//因为right<k,所以j肯定会大于0
        if(j<n&&nums1[i-1]>nums2[j]){//左边最右 反而大于右边最左了
            right=i-1;//下一次从【left,i-1】的区间中检索数组1
        }
        else left=i;//下一次从【left+1,right】
    }
    int i=left;int j=totaleft-i;//这句话很重要 i和j代表的是分界线
    if(i==0) return nums2[j-1];//返回nums1分界线前的元素
    if(j==0) return nums1[i-1];//返回nums2分界线前的元素
    return nums1[i-1]>nums2[j-1]?nums1[i-1]:nums2[j-1];//返回左边的元素中
}

为什么会有第二种方法呢,主函数中找第k个的函数当偶数时会调用两次,但是只要确定了分界线,中位数其实一次就可以确定,即左边最大和右边最小,需要写几次判断而不用再调用函数了,这是显然更比调用两次函数更优秀的方法。


网站公告

今日签到

点亮在社区的每一天
去签到