P3.求两个有序数组的中位数(利扣第四题)

文章详细介绍了如何在两个已排序的数组A和B中找到它们合并后的中位数,无论是等长还是不等长的情况。主要方法包括寻找两个数组的中位数并进行比较,以及通过分割数组来平衡元素数量,确保找到正确位置的中位数。涉及到的算法思路包括二分查找和中位数计算策略。
摘要由CSDN通过智能技术生成

例如:输入:A={2,5,6,8,11},B={3,4,9,12,13}                输出:7.5

若第一个数组有m个元素,第二个数组有n个元素

转换思路:若m+n为基数,则相当于求第k=(m+n+1)/2大的数;若m+n为偶数,则k=(m+n)/2,相当于求第k和k-1的数的中位数

1.若两个序列等长(408统考,2011年)

思路:要选取中间的数,可以不断舍去两边相等长度的序列。由于A,B长度相等,那么合并后长度必为偶数,不断舍去两边,最终会使A,B序列中各自只剩下一个数。用两个序列中位数的方法,每次舍弃各自的一半,这样舍弃的速度较快。

1.分别求A、B两个数组的中位数a,b;

2.若a=b,则a即为所求;若a<b,那么所求中位数肯定在a,b之间,舍去a左侧数据和b右侧数据;同理若a>b,舍去b左侧的数据和a右侧的数据.

3经过处理后,相当于A,B剩余序列的中位数,不断循环之前的操作至两个数组只剩下1个数

之所以能够舍去一半的原因:

        我们在A,B合并后序列中寻找第k大的数时(k大致就是两个序列合并后中位数所在的位置),通常会使用的方法:分别从A,B两个数组首元素开始往后遍历,不断判断A[i]和B[j]的大小,然后将较小的数排除掉,当排除完第k-1个数时就找到了。

以这种思路来看,如图,a6、b6的左侧的数据肯定是先排除掉的。而左侧的数据个数之和肯定小于k(中间数的序号),因此A,B两个数组中必有一个数组中位数左侧的元素会被全部排除掉。当排除掉一个数组的左侧元素时,同理,需要排除另一个数组相同数目的右侧元素,也就是排除右边一半。

根据思路,代码如下

int M_Search(int A[], int B[], int n) {
	int s1 = 0,d1 = n-1,m1, s2 = 0, d2 = n - 1,m2;
//分别代表两个数组的第一个元素,最后一个元素,中位数
	while (s1<d1&&s2<d2) {
		m1 = (s1 + d1) / 2;//求两个数组的中位数,这里偶数中位数暂时用第n/2个元素表示
		m2 = (s2 + d2) / 2;
		if ((n % 2 == 1)) {//若两数组长度为奇数,每个数组各舍去n/2+1个元素
			if (A[m1] < B[m2]) {
				s1 = m1 + 1;
				d2 = m2 - 1;
			}
			if (A[m1] > B[m2]) {
				s2 = m2 + 1;
				d1 = m1 - 1;
			}	
		}
		if ((n % 2 == 0)) {//若两数组长度为偶数,每个数组各舍去n/2个元素
			if (A[m1] < B[m2]) {
				s1 = m1 + 1;
				d2 = m2 ;
			}
			if (A[m1] > B[m2]) {
				s2 = m2 + 1;
				d1 = m1;
			}
		}
	}//循环完后s1=d1,s2=d2
	return (A[s1] + B[d2])/2;
}

2.当两数组长度m,n不等长时

此时再采用第一种方法就不太方便了,因为两个数组中位数所在位置数不一样,不能左右两边排除不相等的序列。

还是相同的思路:若m+n为基数,则相当于求第k=(m+n+1)/2大的数;若m+n为偶数,则k=(m+n)/2,相当于求第k和k-1的数的中位数

这种情况CSDN以及利扣上各路大神所用到的方法最多的就是分割,就是将两个数组分成两部分,如图,使得红色部分的数目r=黄色部分的数目y,m+n为奇数时就是r+1=y;一开始通过中位数划分,然后不断移动分割线,使得红色部分所有元素都小于黄色部分,那么中间数就是红色部分最大数与黄色部分最小数的平均值(或者是黄色部分的第一个数)

我们分别用a,b来表示划分的位置。思路如下:

(1)初始时,令a=(0 + m-1)/ 2,b=(0 + n-1)/ 2,如果a,b为偶数,那么a,b指示的位置为两数组左侧最后一个元素位置;如果为奇数,则有可能指示数组分割线左侧最后一个元素位置,也可能指示数组分割线右侧第一个元素位置。

(2)已知一定有A[a]<A[a+1],B[b ]<B[b + 1]。我们只需不断判断A[a] 与B[b + 1]、B[b] 与A[a + 1]的大小关系,如果A[a] >B[b + 1],则a++;如果B[b] > A[a + 1]则b++,这样不断判断最终使得两数组分割线左侧的元素始终小于分割线右侧的元素。

(3)当分割结束时,判断第k大的数的位置。此时根据两数组元素个数是奇数还是偶数,可以分为四种情况(用红色表示左侧,黄色表示右侧):

1)如图,若两数组大小均为偶数,a,b指示的数均为红色区域两数组最大值,此时第k和k-1的数的中位数={A[a],A[a+1],B[b],B[b+1]}的中位数

2)如图,若A为奇数长数组,B为偶数长数组,a指示的数可能为红色区域最大值或黄色区域最小值,此时第k大数相当于找A[a],B[b]中的较大者或A[a],B[b+1]中的较小者。综合来看,第k大的数={A[a],B[b],B[b+1]}的中位数

3)若B为奇数长数组,A为偶数长数组,这种情况与2)类似,第k大的数={A[a],A[a+1],B[b]}的中位数。

4)如图,若两数组大小均为奇数,a,b指示的数都可能为红色区域最大值或黄色区域最小值(a在红色区域b就在黄色区域,a在黄色区域b就在红色区域,这样可以保持两个区域元素数目相等),则第k大数={A[a-1],B[b-1],A[a],B[b],A[a+1],B[b+1]}的中位数

 

 实现代码如下:

double M_Search(int* A, int m, int* B, int n) {
	int a = (0 + m-1)/ 2;
	int b = (0 + n-1) / 2;
	while (1) {
		if (A[a] > B[b + 1]) {
			b++;
			a--;
		}
		else if (B[b] > A[a + 1]) {
			a++;
			b--;
		}
		else
			break;
	}//完成之后左侧所有元素小于右侧所有元素
	int i = 0, j = 0;
	double k = 0;
	if (m % 2 == 0 && n % 2 == 0) {//若两数组大小均为偶数,则第k大数={A[a],A[a+1],B[b],B[b+1]}的中位数
		i = A[a] > B[b] ? A[a] : B[b];//i存放左侧最大值
		j = A[a + 1] < B[b + 1] ? A[a + 1] : B[b + 1];// j存放右侧最小值
		k = ((double)(i + j)) / 2;
	}
	if (m % 2 == 1 && n % 2 == 0) {//若A为奇数长数组,B为偶数长数组,则第k大数={A[a],B[b],B[b+1]}的中位数
		if (A[a] < B[b])//显然B[b]<B[b+1],只需判断A[a]的位置
			k = B[b];
		else if (A[a] > B[b + 1])
			k = B[b + 1];
		else
			k = A[a];

		}
	if (m % 2 == 0 && n % 2 == 1) {//若A为偶数长数组,B为奇数长数组,则第k大数={A[a],A[a+1],B[b]}的中位数
		if (B[b] < A[a])//显然A[a]<A[a+1],只需判断B[b]的位置
			k = A[a];
		else if (B[b] > A[a+1])
			k = A[a+1];
		else
			k = B[b];
	}
	if (m % 2 == 1 && n % 2 == 1) {//若两数组大小均为奇数,则第k大数={A[a-1],B[b-1],A[a],B[b],A[a+1],B[b+1]}的中位数
		if (A[a] < B[b]) {//a在红色区域b在黄色区域
			i = A[a] > B[b-1] ? A[a] : B[b-1];//i存放左侧最大值
			j = A[a + 1] < B[b] ? A[a + 1] : B[b];// j存放右侧最小值
		}
		else {//a在黄色区域b在红色区域
			i = A[a-1] > B[b] ? A[a-1] : B[b];
			j = A[a] < B[b+1] ? A[a ] : B[b+1];
		}
		k = ((double)(i + j)) / 2;
	}
	return k;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值