【数据结构陈越版笔记】进阶实验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.xdnf.cn/news/1451672.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

使用sherpa-ncnn进行中文语音识别(ubuntu22)

获取该开源项目的渠道&#xff0c;是我在b站上&#xff0c;看到了由csukuangfj制作的一套语音识别视频。以下地址均为csukuangfj在视频中提供&#xff0c;感谢分享&#xff01; 新一代Kaldi RISC-V: VisionFive2 上的实时中英文语音识别_哔哩哔哩_bilibili 开源项目地址&…

SW参数化设计软件 慧德敏学

随着产品设计信息化进程的不断推进&#xff0c;企业运用三维CAD系统进行设计正日趋广泛&#xff0c;三维参数化设计无疑是提高设计效率的好方法之一。所谓参数化设计就是将模型中的约束信息变量化&#xff0c;使之成为可以调整的参数&#xff0c;赋以不同数值就可得到不同大小和…

CPN Tools学习——时间和队列【重要】

-Timed Color Sets 时间颜色集 -Token Stamps 令牌时间戳 -Event Clock 全局/事件/模拟时钟 -Time Delays on Transitions过渡的时间延迟 - List Color Set列表颜色集 - Queue排队 1.时间颜色集 在定时CPN模型令牌中有&#xff1a; &#xff08;1&#xff09;象征性的颜…

【面试干货】抽象类和接口的区别

【面试干货】抽象类和接口的区别 1、抽象类1.1、什么是抽象类&#xff1f;1.2、示例代码 2、接口2.1、什么是接口&#xff1f;2.2、示例代码 3、比较和总结3.1、使用场景3.2、关键区别3.3、代码示例比较 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&am…

【安卓设备】通过adb批量安装apk

1、adb链接设备 H:\tv\apk>adb connect 127.0.0.1:21503 2、批量安装apk 如果地址不一致需要将 H:\tv\apk\ 改成自己的路径地址&#xff0c;同时注意该命令只能安装文件名为英文的不支持中文名称&#xff0c;如果有需要先更改文件名称。 H:\tv\apk>for %f in (H:\tv\a…

VirtualBox、Centos7下安装docker后pull镜像问题

Docker安装篇(CentOS7安装)_docker 安装 centos7-CSDN博客 首先&#xff0c;安装docker可以根据这篇文章进行安装&#xff0c;安装完之后&#xff0c;我们就需要去通过docker拉取相关的服务镜像&#xff0c;然后安装相应的服务容器&#xff0c;比如我们通过docker来安装mysql,…

AI Agent学习系列:微信搭配Agent,让微信秒变特工

在之前的文章里介绍了如何把微信变成高考志愿填报小助手&#xff0c;我已经把这个bot发布到了公众号&#xff0c;大家可以直接在公众号消息输入框里提问即可直接使用&#xff0c;如图&#xff1a; 上面说的bot就是智能体&#xff0c;也叫Agent&#xff0c;和英文里特工是一个单…

【Vue】自学笔记(四)

上一篇&#xff1a;Vue笔记&#xff08;三&#xff09;-CSDN博客 1.VueCli自定义搭建项目 先确保安装了全局工具VueCli 如果没有&#xff0c;则先运行命令 npm i vue/cli -g 选择最后一个自定义搭建项目 选择需要自动搭建的功能 这里我需要router和css预处理器就空格勾选上&…

黄河流域web

1、UNSER的 <?php highlight_file(__FILE__); class Wel {public $fast;public $star;public function __construct(){$this->fast "free_toto";echo "what?";}public function __destruct(){$content $this->star;printf ($content);}pu…

使用ShinyCell展示你的单细胞数据

在我参与发表我的第一篇植物单细胞文章中&#xff0c;我用Shiny开发了一个简单的单细胞可视化网站&#xff0c;目前已经运行了5年了&#xff0c;有上万的访问&#xff0c;唯一的不足就是太简陋。我一直想着能不能找个一个更好的工具进行展示&#xff0c;最近发现了一个工具&…

Druid未授权访问漏洞修复

前言 安全组针对系统漏扫发现系统存在Druid未授权访问&#xff0c;会引发泄露系统敏感信息&#xff0c;漏洞链接为ip:端口/druid/index.html&#xff0c;可以清楚的查看数据库的相关连接信息&#xff0c;如下图所示&#xff1a; 漏洞修复 1、关闭Druid监控页面 在Druid的配…

34 Debian如何配置ELK群集

作者:网络傅老师 特别提示:未经作者允许,不得转载任何内容。违者必究! Debian如何配置ELK群集 《傅老师Debian知识库系列之34》——原创 ==前言== 傅老师Debian知识库特点: 1、拆解Debian实用技能; 2、所有操作在VMware虚拟机实测完成; 3、致力于最终形成Debian知识手…

【TB作品】MSP430 G2553 单片机 口袋板 日历 时钟 闹钟 万年历 电子时钟 秒表显示

文章目录 功能介绍操作方法部分流程图代码录制了一个演示视频可以下载观看 功能介绍 时间与日期显示&#xff1a; 实时显示当前时间&#xff08;小时、分钟、秒&#xff09;和日期&#xff08;年、月、日&#xff09;。 闹钟功能&#xff1a; 设置闹钟时间&#xff08;小时、分…

关于FPGA对 DDR4 (MT40A256M16)的读写控制 2

关于FPGA对 DDR4 &#xff08;MT40A256M16&#xff09;的读写控制 2 语言 &#xff1a;Verilg HDL EDA工具&#xff1a;ISE、Vivado、Quartus II 关于FPGA对 DDR4 &#xff08;MT40A256M16&#xff09;的读写控制 2一、引言二、DDR4的简介四、DDR4 SDRAM状态框图 关键词&#x…

高叶恋情曝光神秘素人男友浮出水面

高叶恋情曝光&#xff0c;神秘素人男友浮出水面&#xff01;在娱乐圈的璀璨星光中&#xff0c;总有一些低调而神秘的恋情&#xff0c;它们如同深藏的宝藏&#xff0c;等待着被发掘。昨日&#xff0c;知名娱乐记者刘大锤的一则爆料&#xff0c;犹如一颗重磅炸弹&#xff0c;炸响…

Go 1.19.4 字符串-Day 06

1. 编码表 计算机中只有数字&#xff08;0和1&#xff09;&#xff0c;如果有一个字符串&#xff08;字符串由字符组成&#xff09;要存储&#xff0c;在内存中该如何表达这个字符串&#xff1f; 那么所有的字符都必须数字化&#xff0c;也就是一个字符对应一个特定的数字&…

腾讯云对象存储不绑定自定义备案域名不给下载应该如何处理?

从2024年1月1日起&#xff0c;腾讯云对象存储&#xff08;COS&#xff09;将实施新政策&#xff1a;新创建的存储桶不再支持使用path-style域名&#xff08;即存储桶绝对路径&#xff09;。此外&#xff0c;使用默认域名访问的新存储桶将不再支持任意类型文件的预览&#xff0c…

Mac error:0308010C:digital envelope routines::unsupported

背景&#xff1a; node版本20.14.0 执行npm run start命令的时候报错 问题&#xff1a; error:0308010C:digital envelope routines::unsupported 分析&#xff1a; 出现这个错误是因为 node.js V17版本中最近发布的OpenSSL3.0, 而OpenSSL3.0对允许算法和密钥大小增加了严…

VMware安装ubuntu22.04虚拟机超详细图文教程

一 、下载镜像 下载地址&#xff1a;Index of /ubuntu-releases/22.04.4/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 二、创建虚拟机 打开VMware点击左上角文件&#xff0c;创建新的虚拟机&#xff0c;打开后如下图&#xff1a; 下一步&#xff0c;镜像文件就是…

视频号怎么保存视频到手机?推荐4种方法!

短视频已经成为了网友们的新宠&#xff0c;那么对于我们这些普通人来说&#xff0c;如何能够轻松提取视频号上的视频呢&#xff1f;今天&#xff0c;就让我们一起来探讨一下视频号视频提取各种方法和工具&#xff01; 虽然视频号视频的保存方式多种多样&#xff0c;但为了照顾那…