【数据结构陈越版笔记】进阶实验1-3.1:两个有序序列的中位数

我这答案做的可能不对,如果不对,欢迎大家指出错误,思路大部分直接写在注释中了。

进阶实验1-3.1:两个有序序列的中位数

已知有两个等长的非降序序列S1, S2, 设计函数求S1与S2并集的中位数。有序序列 A 0 , A 1 , . . . , A n − 1 A_{0},A_{1},...,A_{n-1} A0,A1,...,An1的中位数 A ( n − 1 ) / 2 A_{(n-1)/2} A(n1)/2的值,即第 ⌊ ( N + 1 ) / 2 ⌋ \lfloor(N+1) / 2\rfloor ⌊(N+1)/2个数( A 0 A_{0} A0为第一个数)

输入格式:

输入分三行。第一行给出序列的公共长度N(0<N≤100000),随后每行输入一个序列的信息,即N个非降序排列的整数。数字用空格间隔。

输出格式:

在一行中输出两个输入序列的并集序列的中位数。

输入样例1:

5
1 3 5 7 9
2 3 4 5 6

输出样例1:

4

思路与答案

方法一

根据题意可以直观地想到,先开设一新数组求两序列的并集 S 3 S_{3} S3,然后取 S 3 S_{3} S3的中间项即为中位数。保存并集 S 3 S_{3} S3需要额外空间 O ( N ) O(N) O(N)。由于原序列有序,求并集时可从两序列列首开始比较,不断将较小值移入新数列,需 O N O_{N} ON时间;最后取中项只需 O ( 1 ) O(1) O(1)时间。故时间复杂度和空间复杂度均为 O ( N ) O(N) O(N)

#include<stdio.h>
#include<stdlib.h>//合并两个相同长度的有序序列,并生成新序列
//l1为序列1,l2为序列2,len为序列长度
int* mergeOrders(int* l1, int* l2, int len)
{//开辟新的数组l3当作两个序列的并集int* l3 = (int*)malloc(2 * len * (sizeof(int)));//内存不够开辟失败if(l3 == NULL){return NULL;}int i = 0;// 遍历l1用的变量int j = 0;// 遍历l2用的变量int k = 0;// 遍历l3用的变量int min = 0;// 取二者最小值的变量//不断地将最小值移入新序列while(k<2 * len * (sizeof(int))){//i和j在数组长度范围内,选最小值赋值给l3[k]if(i<len && j<len){if(l1[i] < l2[j]){min = l1[i];i++; //l1当前元素已加入l3,进入l1下一个元素去对比,即i++}else{min = l2[j];j++; //同i++原理套用在l2上}l3[k] = min;}//l2已经遍历完成,还剩下l1,让l1剩下的全部赋值给l3[k]及其后面的else if (i<len && j>=len){l3[k] = l1[i];i++;}//l1遍历完成,后边只让l2赋值else if (i>=len && j<len){l3[k] = l2[j];j++;}else{//全部赋值完成,跳出循环break;}k++;}return l3;
}int main()
{int len = 0;//数组长度lenscanf("%d", &len); //输入数组长度int* l1 = (int*)malloc((sizeof(int)) * len);//开辟内存空间失败直接返回if(l1 == NULL){return 0;}int* l2 = (int*)malloc((sizeof(int)) * len);if(l2 == NULL){return 0;}int save = 0;//存储输入的数据//循环地输入数组for(int i = 0; i < len; i++){save = 0; //scanf("%d", &save);char c=getchar();//回收缓存区的回车键l1[i]=save;//b把得到的数赋值给数组if(c=='\n')//判断是否为空,重新循环{break;}}for(int i = 0; i < len; i++){save = 0; //scanf("%d", &save);char c=getchar();//回收缓存区的回车键l2[i]=save;//b把得到的数赋值给数组if(c=='\n')//判断是否为空,重新循环{break;}}int* l3 = mergeOrders(l1, l2, len);//合并后的l3//因为l3也是有序的,输出其中位数即可//下标应该是2*len/2 - 1= len -1,因为下标从0开始printf("%d", l3[len - 1]);return 0;
}

提交结果:

方法二

方法一需要新数组保存整个并集,而实际上求并集过程只需要比较两个序列当前的数字,所取到的第N个数即为中位数,所以前面N-1个数无需保存,只在取到第N个数时输出即可,因此可将空间复杂度改进 O ( 1 ) O(1) O(1)

#include<stdio.h>
#include<stdlib.h>//直接返回两个序列的中位数
//l1为序列1,l2为序列2,len为序列长度
int Median(int* l1, int* l2, int len)
{int i = 0;// 遍历l1用的变量int j = 0;// 遍历l2用的变量int k = 0;// 用来记录是否到第len个int min = 0;// 取二者最小值的变量//不断地将最小值移入新序列//k只需要到len(N)即可,i和j这种情况下都是<=kwhile(k< len){//i和j在数组长度范围内,选最小值赋值给l3[k]if(i<len && j<len){if(l1[i] < l2[j]){min = l1[i];i++; //l1当前元素已是最小值,进入l1下一个元素去对比,即i++}else{min = l2[j];j++; //同i++原理套用在l2上}}k++;}return min;
}int main()
{int len = 0;//数组长度lenscanf("%d", &len); //输入数组长度int* l1 = (int*)malloc((sizeof(int)) * len);//开辟内存空间失败直接返回if(l1 == NULL){return 0;}int* l2 = (int*)malloc((sizeof(int)) * len);if(l2 == NULL){return 0;}int save = 0;//存储输入的数据//循环地输入数组for(int i = 0; i < len; i++){save = 0; //scanf("%d", &save);char c=getchar();//回收缓存区的回车键l1[i]=save;//b把得到的数赋值给数组if(c=='\n')//判断是否为空,重新循环{break;}}for(int i = 0; i < len; i++){save = 0; //scanf("%d", &save);char c=getchar();//回收缓存区的回车键l2[i]=save;//b把得到的数赋值给数组if(c=='\n')//判断是否为空,重新循环{break;}}printf("%d", Median(l1, l2, len));return 0;
}

提交结果:

方法三

由于原序列有序,可以借鉴二分法,假设 S 1 S_{1} S1 A 0 , A 1 , . . . , A N − 1 A_{0},A_{1},...,A_{N-1} A0,A1,...,AN1 S 2 S_{2} S2 B 0 , B 1 , . . . . , B N − 1 B_{0},B_{1},....,B_{N-1} B0,B1,....,BN1,其中 S 1 S_{1} S1中位数为 A ( N − 1 ) / 2 A_{(N-1)/2} A(N1)/2, S 2 S_{2} S2中位数为 B ( N − 1 ) / 2 B_{(N-1)/2} B(N1)/2。若记最终并集的中位数为 M M M,则可以证明 min ⁡ ( A ( N − 1 ) / 2 , B ( N − 1 ) / 2 ) ⩽ M ⩽ max ⁡ ( A ( N − 1 ) / 2 , B ( N − 1 ) / 2 ) \min \left(A_{(N-1) / 2}, B_{(N-1) / 2}\right) \leqslant M \leqslant \max \left(A_{(N-1) / 2}, B_{(N-1) / 2}\right) min(A(N1)/2,B(N1)/2)Mmax(A(N1)/2,B(N1)/2)。因此,当 N N N为1时,中位数 M = min ⁡ ( A 0 , B 0 ) M=\min(A_{0},B_{0}) M=min(A0,B0)。一般情况下,若两中位数相等,即 A ( N − 1 ) / 2 = = B ( N − 1 ) / 2 A_{(N-1)/2}==B_{(N-1)/2} A(N1)/2==B(N1)/2,则 M = A ( N − 1 ) / 2 M=A_{(N-1)/2} M=A(N1)/2;若不相等,即 A ( N − 1 ) / 2 ! = B ( N − 1 ) / 2 A_{(N-1)/2}!=B_{(N-1)/2} A(N1)/2!=B(N1)/2,不妨假设 A ( N − 1 ) / 2 < B ( N − 1 ) / 2 A_{(N-1)/2}<B_{(N-1)/2} A(N1)/2<B(N1)/2,可证明,序列 S 1 S_{1} S1 A ( N − 1 ) / 2 A_{(N-1)/2} A(N1)/2左边除 k k k个数,同时序列 S 2 S_{2} S2 B ( N − 1 ) / 2 B_{(N-1)/2} B(N1)/2右边也去除 k k k个数后,新序列并集的中位数依然为 M M M。因此,可转换为求两个新序列的中位数。

#include<stdio.h>
#include<stdlib.h>
//选出两个整数之间的最小值
int Min(int a, int b)
{if (a > b){return b;}else{return a;}
}
//直接返回两个序列的中位数
//l1为序列1,l2为序列2,len为序列长度
int Median(int* l1, int* l2, int len)
{//模仿二分查找int i1 = 0; // l1数组的开始元素的下标int i2 = 0; // l2数组的开始元素的下标int j1 = len - 1; // l1数组最后的元素的下标int j2 = len - 1; // l2数组最后的元素的下标int mid1 = (j1 + i1) >> 1; // l1数组的中位数下标int mid2 = (j2 + i2) >> 1; // l2数组的中位数下标//如果两个数组的中位数相等,直接返回该中位数if (l1[mid1] == l2[mid2]){return l1[mid1];}//左闭右开while (i1 <= j1){//计算新的中位数的下标mid1 = (j1 + i1) >> 1;mid2 = (j2 + i2) >> 1;/** 假设按测试样例1* Step1:l1={1, 3, 5, 7, 9},i1=0,j1=4l2={2, 3, 4, 5, 6},i2=0,j2=4mid1 = 2mid2 = 2len为奇数l1[mid1]=l1[2]=3 < l2[mid1]=l2[2]=4i1 = mid1 = 2j2 = mid2 = 2Step2:l1={X, X, 5, 7, 9},i1=2,j1=4l2={2, 3, 4, X, X},i2=0,j2=2mid1 = 3mid2 = 1len为奇数l1[mid1]=l1[3]=7>l2[mid2]=l2[1]=3j1=mid1=3i2=mid2=1Step3:l1={X, X, 5, 7, X},i1=2,j1=3l2={X, 3, 4, X, X},i2=1,j2=2mid1=2mid2=1len为偶数l1[mid]=l1[2]=5>l2[mid]=l2[1]=3j1=mid1=2i2=mid2+1=2Step4:l1={X, X, 5, X, X},i1=2,j1=2l2={X, X, 4, X, X},i2=2,j2=2mid1=2mid2=2len为奇数如果再执行代码,将陷入死循环,所以左闭右闭情况下,应该在while循环最开始的地方加入一个判断条件判断当前是否两个序列都剩下一个元素,如果剩下一个元素,跳出循环。*/if (j1 - i1 + 1 == 1){break;}//如果长度是偶数,则(此处不能用len % 2 == 0,因为每次二分后,数组的长度在变化)if ((j1 - i1 + 1) % 2 == 0){//如果l1[mid]>l2[mid],则丢掉l1[mid](不含l1[mid])右边的数,和l2[mid](含l2[mid])左边的数//这么做是为了保证丢弃的数的个数是一致的,都是题目中说的k个//比如l1={1,2,3,4}和l2={5,6,7,8},长度为4,是偶数//l1[mid]<l2[mid],丢弃l2中的{7,8},然后丢弃l1中的{1,2},如果只丢弃到if (l1[mid1] > l2[mid2]){j1 = mid1;i2 = mid2 + 1;}else if (l1[mid1] < l2[mid2]){i1 = mid1 + 1;j2 = mid2;}else //中位数相等直接返回{break;}}else//奇数情况{//如果l1[mid]>l2[mid],则丢掉l1[mid](不含l1[mid])右边的数,和l2[mid](不含l2[mid])左边的数//因为是奇数,所以左右两边去掉的数的个数是一样的if (l1[mid1] > l2[mid2]){j1 = mid1;i2 = mid2;}else if (l1[mid1] < l2[mid2]){i1 = mid1;j2 = mid2;}else{break;}}//去掉k个数的过程相当于不断地二分}//最后二分到每个序列剩下一个的情况下,返回最小的return Min(l1[mid1], l2[mid2]);
}int main()
{int len = 0;//数组长度lenscanf("%d", &len); //输入数组长度int* l1 = (int*)malloc((sizeof(int)) * len);//开辟内存空间失败直接返回if (l1 == NULL){return 0;}int* l2 = (int*)malloc((sizeof(int)) * len);if (l2 == NULL){return 0;}int save = 0;//存储输入的数据//循环地输入数组for (int i = 0; i < len; i++){save = 0; //scanf("%d", &save);char c = getchar();//回收缓存区的回车键l1[i] = save;//b把得到的数赋值给数组if (c == '\n')//判断是否为空,重新循环{break;}}for (int i = 0; i < len; i++){save = 0; //scanf("%d", &save);char c = getchar();//回收缓存区的回车键l2[i] = save;//b把得到的数赋值给数组if (c == '\n')//判断是否为空,重新循环{break;}}printf("%d", Median(l1, l2, len));return 0;
}

提交结果:

时间复杂度

方法1的时间复杂度为 O ( N ) O(N) O(N),空间复杂度为 O ( N ) O(N) O(N)
方法2的时间复杂度为 O ( N ) O(N) O(N),空间复杂度为 O ( 1 ) O(1) O(1)
方法3的时间复杂度为 O ( l o g N ) O(logN) O(logN),空间复杂度为 O ( 1 ) O(1) O(1)

实验思考题

(1)如何证明 min ⁡ ( A ( N − 1 ) / 2 , B ( N − 1 ) / 2 ) ⩽ M ⩽ max ⁡ ( A ( N − 1 ) / 2 , B ( N − 1 ) / 2 ) \min \left(A_{(N-1) / 2}, B_{(N-1) / 2}\right) \leqslant M \leqslant \max \left(A_{(N-1) / 2}, B_{(N-1) / 2}\right) min(A(N1)/2,B(N1)/2)Mmax(A(N1)/2,B(N1)/2)?【提示:反证法。例如,若中位数小于较小值,则有超过一半的数大于它,矛盾】
【证明】反证法,假设 M > max ⁡ ( A ( N − 1 ) / 2 , B ( N − 1 ) / 2 ) M> \max \left(A_{(N-1) / 2}, B_{(N-1) / 2}\right) M>max(A(N1)/2,B(N1)/2) M < min ⁡ ( A ( N − 1 ) / 2 , B ( N − 1 ) / 2 ) M< \min \left(A_{(N-1) / 2}, B_{(N-1) / 2}\right) M<min(A(N1)/2,B(N1)/2)
S 1 S_{1} S1 S 2 S_{2} S2的并集 S 3 S_{3} S3可以由 C 0 , C 1 , . . . , C k − 1 C_{0},C_{1},...,C_{k-1} C0,C1,...,Ck1表示,则 k = 2 n k=2n k=2n,则 S 3 S_{3} S3一半的项数为 k 2 = n \frac{k}{2}= n 2k=n
由中位数的定义:中位数(median)是将一组数据按照从小到大的顺序排列(或者从大到小的顺序也可以)之后处在数列中点位置的数值。对于一组有限个数的数据来说,它们的中位数是这样的一种数:这群数据里的一半的数据比它大,而另外一半数据比它小
M > max ⁡ ( A ( N − 1 ) / 2 , B ( N − 1 ) / 2 ) M> \max \left(A_{(N-1) / 2}, B_{(N-1) / 2}\right) M>max(A(N1)/2,B(N1)/2) M < min ⁡ ( A ( N − 1 ) / 2 , B ( N − 1 ) / 2 ) M< \min \left(A_{(N-1) / 2}, B_{(N-1) / 2}\right) M<min(A(N1)/2,B(N1)/2)可知,
M > { A ( n − 1 ) / 2 , A ( ( n − 1 ) / 2 ) + 1 + . . . + , A n − 1 } M> \{A_{(n-1) / 2},A_{((n-1) / 2)+1}+...+,A_{n-1}\} M>{A(n1)/2,A((n1)/2)+1+...+,An1} M > { B ( n − 1 ) / 2 , B ( ( n − 1 ) / 2 ) + 1 + . . . + , B n − 1 } M> \{B_{(n-1) / 2},B_{((n-1) / 2)+1}+...+,B_{n-1}\} M>{B(n1)/2,B((n1)/2)+1+...+,Bn1},即 S 3 S_{3} S3中小于 M M M的项数为 2 × ( n − 1 − n − 1 2 + 1 ) = n + 1 > n = k 2 2\times(n-1-\frac{n-1}{2}+1)=n+1>n= \frac{k}{2} 2×(n12n1+1)=n+1>n=2k,即在 S 3 S_{3} S3中有超过一半的数据小于 M M M
M < { A ( n − 1 ) / 2 , A ( ( n − 1 ) / 2 ) − 1 + . . . + , A 0 } M< \{A_{(n-1) / 2},A_{((n-1) / 2)-1}+...+,A_{0}\} M<{A(n1)/2,A((n1)/2)1+...+,A0} M < { B ( n − 1 ) / 2 , B ( ( n − 1 ) / 2 ) − 1 + . . . + , B 0 } M< \{B_{(n-1) / 2},B_{((n-1) / 2)-1}+...+,B_{0}\} M<{B(n1)/2,B((n1)/2)1+...+,B0},即 S 3 S_{3} S3中大于 M M M的项数为 2 × ( n − 1 2 − 0 + 1 ) = n + 1 > n = k 2 2\times(\frac{n-1}{2}-0+1)=n+1>n= \frac{k}{2} 2×(2n10+1)=n+1>n=2k,即在 S 3 S_{3} S3中有超过一半的数据大于 M M M
这两条均不满足中位数的定义,故矛盾
min ⁡ ( A ( N − 1 ) / 2 , B ( N − 1 ) / 2 ) ⩽ M ⩽ max ⁡ ( A ( N − 1 ) / 2 , B ( N − 1 ) / 2 ) \min \left(A_{(N-1) / 2}, B_{(N-1) / 2}\right) \leqslant M \leqslant \max \left(A_{(N-1) / 2}, B_{(N-1) / 2}\right) min(A(N1)/2,B(N1)/2)Mmax(A(N1)/2,B(N1)/2),得证。

(2)如何证明原序列的并集的中位数等于截取后新序列的并集的中位数?【提示:可证明,非降序序列 S S S中,在中位数 x x x左边和右边各除掉 k k k个数后(即除去 k k k个小于 x x x的数与 k k k个大于 x x x的数),中位数保持不变。】
【证明】先证明非降序序列 S S S中,在中位数 x x x左边和右边各除掉 k k k个数后(即除去 k k k个小于 x x x的数与 k k k个大于 x x x的数),中位数保持不变。

【GPT】为了证明非降序序列 S S S中,在中位数 x x x左边和右边各除掉 k k k个数后,中位数保持不变,我们可以按照以下步骤进行推导:
第一步,根据题目条件,我们设非降序序列 S S S的长度为 n n n,中位数为 x x x
第二步,根据中位数的定义,我们知道中位数 x x x将序列 S S S分为两部分,左边部分有 n 2 \frac{n}{2} 2n个元素(或 n − 1 2 \frac{n-1}{2} 2n1个元素,当 n n n为奇数时),右边部分也有 n 2 \frac{n}{2} 2n个元素(或 n − 1 2 \frac{n-1}{2} 2n1个元素,当 n n n为奇数时)。
第三步,我们考虑在 x x x左边和右边各除掉 k k k个数的情况。由于 x x x是中位数,所以左边部分至少有 n 2 \frac{n}{2} 2n个数小于 x x x,右边部分至少有 n 2 \frac{n}{2} 2n个数大于 x x x。因此,只要 k ≤ n 2 k \leq \frac{n}{2} k2n(当 n n n为奇数时, k ≤ n − 1 2 k \leq \frac{n-1}{2} k2n1),我们就可以在 x x x的左边和右边各除掉 k k k个数。
第四步,除掉 k k k个数后,左边部分剩余 n 2 − k \frac{n}{2} - k 2nk个数(或 n − 1 2 − k \frac{n-1}{2} - k 2n1k个数,当 n n n为奇数时),右边部分也剩余 n 2 − k \frac{n}{2} - k 2nk个数(或 n − 1 2 − k \frac{n-1}{2} - k 2n1k个数,当 n n n为奇数时)。由于 S S S是非降序序列,所以新的中位数仍然是 x x x,因为它仍然将剩余的部分分为两个数量相等的部分(或数量相差1的部分,当 n n n为奇数时)。
因此,我们证明了非降序序列 S S S中,在中位数 x x x左边和右边各除掉 k k k个数后,中位数保持不变。

在非降序序列 S 1 S_{1} S1 S 2 S_{2} S2中,不妨设 S 1 S_{1} S1的中位数 A ( n − 1 ) / 2 A_{(n-1) / 2} A(n1)/2小于 S 2 S_{2} S2的中位数 B ( n − 1 ) / 2 B_{(n-1) / 2} B(n1)/2,则在 S 1 S_{1} S1的中位数左边去掉 k k k个数后, S 1 = { A ( n − 1 ) / 2 , A ( ( n − 1 ) / 2 ) + 1 , . . . , A n − 1 } S_{1}=\{A_{(n-1) / 2},A_{((n-1) / 2)+1},...,A_{n-1}\} S1={A(n1)/2,A((n1)/2)+1,...,An1},在 S 2 S{2} S2的中位数右侧去掉 k k k个数后, S 2 = { B 0 , B 1 , . . . , B ( n − 1 ) / 2 } S_{2}=\{B_{0},B_{1},...,B_{(n-1) / 2}\} S2={B0,B1,...,B(n1)/2},,由于 S 1 S_{1} S1 S 2 S_{2} S2是非降序序列,则相当于合并后的序列从左侧和右侧去掉 k k k个数,中位数不变,证毕。

(3)求出方法三的时间与空间复杂度。
【答】每次都去掉两个序列的中位数左右侧的数,相当于不断地二分,时间复杂度就是 O ( l o g N ) O(logN) O(logN),其实这个我没列出差分方程,评论区若有大佬看出来,可以帮忙列一下,没有开辟规模为N的数组的内存空间,所以空间复杂度为 O ( 1 ) O(1) O(1)

(4)若输入两序列不等长,该如何修改程序?
【答】实际上本体就是LeetCode4寻找两个正序数组的中位数稍微魔改,数组和为偶数的时候,返回的不是均值,而是两个数组在分割线左侧的第一个元素的最大值,详见【LeetCode】4,寻找两个正序数组中的中位数,代码如下:

#include<stdio.h>
#include<stdlib.h>
//选出两个整数之间的最小值和最大值
int Min(int a, int b)
{if (a > b){return b;}else{return a;}
}
int Max(int a, int b)
{if (a < b){return b;}else{return a;}
}
int findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size) {//如果有空数组,则直接返回非空数组的中位数if (nums1 == NULL || nums1Size == 0){//奇数长度返回中间,偶数取中间两个均值if (nums2Size % 2 == 0){int mid = (nums2Size - 1) >> 1; //返回的是下标中点,不是数量中点return (double)((nums2[mid] + nums2[mid + 1]) / 2.0);}else{return nums2[nums2Size / 2];}}if (nums2 == NULL || nums2Size == 0){//奇数长度返回中间,偶数取中间两个均值if (nums1Size % 2 == 0){int mid = (nums1Size - 1) >> 1;return (double)((nums1[mid] + nums1[mid + 1]) / 2.0);}else{return nums1[nums1Size / 2];}}//如果第一个数组的长度大于第二个数组的长度,那就交换以下,让较短的数组成为第一个数组if (nums1Size > nums2Size){//交换数组指针int* temp = nums1;nums1 = nums2;nums2 = temp;//交换数组长度变量int tmp = nums1Size;nums1Size = nums2Size;nums2Size = tmp;}//数组1和数组2的长度分别用变量m和n表示int m = nums1Size;int n = nums2Size;//分割线左侧元素个数int totalLeft = (m + n + 1) >> 1; //向右移动1位相当于除2//在nums1的区间[0, m]里查找恰当的分割线。//分割线在第一个数组左边的最大值nums1[i-1]要小于等于分割线在第二个数组右边的最小值nums2[j]//并且,分割线在第二个数组左边的最大值nums2[j-1]要小于等于分割线在第一个数组右边的最小值nums1[i]//这个就是我们分析出来的交叉的不等关系int left = 0;int right = m;//以长度最小的数组做循环变量的二分while (left < right){//分割线在第一个数组的下标//+1的原因://比如偶数长度的数组[1, 2, 3, 4]//开始的(left + right) / 2为1,其实我们的i应该是分割线右侧,即i为2,我们应该向上取整,向上取整就应该+1后再除2//奇数长度因为我们也要将i设置为中位数右侧第一个元素下标的值(对应上面文中的规则)int i = (left + right + 1) >> 1;//分割线在第二个数组的下标//比如两个数组[1, 2]和[3, 4, 5, 6]//i=0,则我们没有把第二个数组的开始和结束下标记作left和right//只能通过totalLeft间接推出//totalLeft=3,totalLeft是两个数组合并后的相对的左侧数组的元素个数//totalLeft-i相当于把左侧数组中第一个数组左侧的元素数减掉,只剩下第二个数组左侧的元素数//第二个数组左侧元素的个数恰好就是第二个数组的分割线的位置jint j = totalLeft - i;// 第一个数组中分割线左侧的元素大于第二个数组中分割线右侧的元素// 说明分割线在第一个数组上的位置太靠右了,所以分割线位置在i这个位置的左侧(不包括i)// 所以下一轮位置,所以下一轮搜索区间是[left, i - 1]if (nums1[i - 1] > nums2[j]){right = i - 1;}//else// 如果恰好满足条件了,则将第一个数组右侧的元素和第二个数组左侧的元素算作两个新数组继续二分{left = i;}}//确定二分到最后的数组的分割线int i = left;int j = totalLeft - i;//求出第一个数组的分割线的左侧的最大值和右侧的最小值的变量//第二个数组以此类推//先初始化为0int nums1LeftMax = 0;int nums1RightMin = 0;int nums2LeftMax = 0;int nums2RightMin = 0;//如果最后的分割线i为0时,说明第一个数组分割线左侧数组的最大值不存在,则令此时的nums1LeftMax = INT_MIN//如果最后的分割线i为m时,说明第一个数组分割线右侧数组的最小值不存在,则令此时的nums1RightMin = INT_MAX//同理第二个数组j为0或者n时,以此类推if (i == 0){nums1LeftMax = INT_MIN;nums1RightMin = nums1[i];}else if (i == m){nums1LeftMax = nums1[i - 1];nums1RightMin = INT_MAX;}else{nums1LeftMax = nums1[i - 1];nums1RightMin = nums1[i];}if (j == 0){nums2LeftMax = INT_MIN;nums2RightMin = nums2[j];}else if (j == n){nums2LeftMax = nums2[j - 1];nums2RightMin = INT_MAX;}else{nums2LeftMax = nums2[j - 1];nums2RightMin = nums2[j];}//魔改代码的地方return Max(nums1LeftMax, nums2LeftMax);
}int main()
{int len1 = 0;//数组1长度len1scanf("%d", &len1); //输入数组1长度int* l1 = (int*)malloc((sizeof(int)) * len1);//开辟内存空间失败直接返回if (l1 == NULL){return 0;}int save = 0;//存储输入的数据//循环地输入数组for (int i = 0; i < len1; i++){save = 0; //scanf("%d", &save);char c = getchar();//回收缓存区的回车键l1[i] = save;//b把得到的数赋值给数组if (c == '\n')//判断是否为空,重新循环{break;}}int len2 = 0;//数组2长度len2scanf("%d", &len2); //输入数组2长度int* l2 = (int*)malloc((sizeof(int)) * len2);if (l2 == NULL){return 0;}for (int i = 0; i < len2; i++){save = 0; //scanf("%d", &save);char c = getchar();//回收缓存区的回车键l2[i] = save;//b把得到的数赋值给数组if (c == '\n')//判断是否为空,重新循环{break;}}printf("%d", findMedianSortedArrays(l1, len1, l2, len2));return 0;
}

简单测试一下:

拼起来看看:
[1, 1, 1, 2, 3, 4, 6]
中位数是2,没错

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/27318.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ES升级--05--快照生成 和备份

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 备份ES数据1.关闭集群自动均衡2.执行同步刷新3.停止集群节点的Elasticsearch服务4.修改Elasticsearch配置文件&#xff0c;开启快照功能&#xff0c;配置仓库目录为…

微信群发机器人.使用指南.

0.简介 1.介绍 微信群发机器人是用来群发微信消息的工具,通过控制电脑的键盘和鼠标操作微信app来实现群发.支持的消息类型有:文字,图片,视频,文件,小程序,位置等. 群发机器人也可以将微信联系人中的信息保存到电脑csv表格中,以供分析. 因其是通过模拟用户操作鼠标键盘来实现群…

the histogram of cross-entropy loss values 交叉熵损失值的直方图以及cross-entropy loss交叉熵损失

交叉熵损失值的直方图在机器学习和深度学习中有几个重要的作用和用途&#xff1a; 评估模型性能: 直方图可以帮助评估模型在训练数据和测试数据上的性能。通过观察损失值的分布&#xff0c;可以了解模型在不同数据集上的表现情况。例如&#xff0c;损失值分布的形状和范围可以反…

C++中extern “C“的用法

目的 extern "C"是经常用到的东西&#xff0c;面试题目也经常出现&#xff0c;然则&#xff0c;实际用时&#xff0c;还是经常遗忘&#xff0c;因此&#xff0c;深入的了解一下&#xff0c;以增强记忆。 extern "C"指令非常有用&#xff0c;因为C和C的近亲…

Android MediaMetadataRetriever获取视频宽高,Java

Android MediaMetadataRetriever获取视频宽高&#xff0c;Java public static int[] getVideoSize(Context ctx, Uri uri) {MediaMetadataRetriever retriever new MediaMetadataRetriever();int[] size {-1, -1}; //宽&#xff0c;高try {retriever.setDataSource(ctx, uri)…

双向转发检测BFD(学习笔记)

定义 双向转发检测BFD&#xff08;Bidirectional Forwarding Detection&#xff09;是一种全网统一的检测机制&#xff0c;用于快速检测、监控网络中链路或者IP路由的转发连通状况 BFD检测机制 BFD的检测机制是两个系统建立BFD会话&#xff0c;并沿它们之间的路径周期性发送B…

Java 开发实例:Spring Boot+AOP+注解+Redis防重复提交(防抖)

文章目录 1. 环境准备2. 引入依赖3. 配置Redis4. 创建防重复提交注解5. 实现AOP切面6. 创建示例Controller7. 测试8. 进一步优化8.1 自定义异常处理8.2 提升Redis的健壮性 9. 总结 &#x1f389;欢迎来到Java学习路线专栏~探索Java中的静态变量与实例变量 ☆* o(≧▽≦)o *☆嗨…

King Media 8.2 中文版安装

King Media-Viral Magazine News Video是一个用于架设社交网站的php脚本&#xff0c;能让您创建一个视频、新闻和图像的新颖社交网站。 功能 支持&#xff1a;从Url、Youtube、Vimeo、Vine、Instagram、Metacafe、DailyMotion上传和分享图片通过Facebook、谷歌、雅虎、Github和…

EC20通信模块升级失败 Quectel QDLoader 9008

这里写自定义目录标题 usb驱动下载固件和升级软件下载开始升级上述过程升级失败&#xff0c;出现Quectel QDLoader 9008寻找解决方案&#xff0c;事了QPS t不行&#xff0c;最终使用这个Quectel_Customer_FW_Download_Tool软件解决下载链接&#xff1a; 所有下载驱动、固件、软…

C++数据结构02 队列及其应用

目录 队列及其特点 利用数组模拟队列的基本操作 创建队列 空队条件 元素入队 元素出队 模拟超市收银问题 队列操作 初始化 入队操作 出队操作 取出队首元素 STL模板中队列的基本使用 训练&#xff1a;约瑟夫问题 参考程序 队列及其特点 队列是一种特殊的线性表&am…

Mac如何卸载掉系统自带的预装软件吗 Mac第三方软件无法卸载是为什么 macbook系统软件怎么删除?

在使用Mac电脑时&#xff0c;有时候我们会发现系统预装的一些应用并不常用或者不符合个人需求&#xff0c;想要将它们卸载掉。然而&#xff0c;对于系统自带的软件&#xff0c;卸载并不简单&#xff0c;需要谨慎对待以免影响系统稳定性和功能正常运行。 一、Mac可以卸载掉系统自…

Android安全开发之 Provider 组件安全

Android系统中的Content Provider组件是一种用于在不同应用之间共享数据的机制。它提供了一种安全、可控的方式&#xff0c;允许应用访问其他应用的数据。然而&#xff0c;如果Provider组件的安全措施没有得到妥善实现&#xff0c;则可能会导致严重的安全漏洞&#xff0c;例如数…

比利时海外媒体宣发,发稿促进媒体通稿发布新形势-大舍传媒

引言 随着全球化的推进&#xff0c;海外媒体的影响力也日益增强。在这一背景下&#xff0c;比利时海外媒体的宣发工作成为了媒体通稿发布的新形势。大舍传媒作为一家专注于宣传推广的公司&#xff0c;一直致力于与比利时博伊克邮报&#xff08;boicpost&#xff09;合作&#…

用数据说话,效果好上一万倍,不是空口说白话的“好很多”

作为一名大数据开发者&#xff0c;我深知数据的有很大的魔力&#xff08;我这句话就没用数据&#xff0c;听上去很无力&#xff09;。数据不仅仅是数字和图表&#xff0c;它还能赋予我们强大的说服力和权威感。让我给你详细讲讲数据如何让理论插上翅膀。 目录 数据的“靠谱”…

文字悬停效果

文字悬停效果 效果展示 CSS 知识点 CSS 变量使用回顾-webkit-text-stroke 属性的运用与回顾 页面整体结构实现 <ul><li style"--clr: #e6444f"><a href"#" class"text">First</a></li><li style"--cl…

如何看待有企业使用AI写代码,6个月研发提效超20%,未来AI对程序员会有多大影响?

AIGC对程序员来说&#xff0c;有远虑&#xff0c;无近忧。 目前看来&#xff0c;AI是程序员编写代码很好的助手&#xff0c;尤其在代码补全、照样子写代码、生成注释及文档等方面效果非常好&#xff0c;还有能省去很多查api的时间。 但即便如此&#xff0c;它也仅仅能解决造轮子…

ripro主题如何使用memcached来加速

ripro主题是个很不错的资源付费下载主题。主题自带了缓存加速开关&#xff0c;只要开启了缓存加速功能&#xff0c;正常情况下能让网站访问的速度提升很大。 但好多人这么做了却发现没啥加速效果&#xff0c;原因就在于wordpress里缺少了memcache文件。只需要把object-cache.ph…

CATIA软件 输入无参格式还原成为有参模型教程

上图为零件设计中特征辨识的命令栏&#xff0c;此次分享是对手动/自动特征辨识的命令应用&#xff0c;作个简单操作的分享了解。在CATIA软件中把输入的无参模型通过特征化后转化为有参模型的过程。其实CATIA软件早有这个功能提供给用户使用了&#xff0c;可能大家很少用。有了手…

java:spring使用【XXXPostProcessor】添加bean定义,修改bean定义、代理bean

# 项目代码资源&#xff1a; 可能还在审核中&#xff0c;请等待。。。 https://download.csdn.net/download/chenhz2284/89433361 # 项目代码 【pom.xml】 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-start…