问题描述
设X[ 0 : n - 1]和Y[ 0 : n – 1 ]为两个数组,每个数组中含有n个已排好序的数。找出X和Y的2n个数的中位数。
数据输入
由文件input.txt提供输入数据。文件的第1行中有1个正整数n(n<=200),表示每个数组有n个数。接下来的两行分别是X,Y数组的元素。
结果输出
程序运行结束时,将计算出的中位数输出到文件output.txt中。
输入文件示例 | 输出文件示例 |
input.txt | output.txt |
3 5 15 18 3 14 21 | 14 |
实现提示
比较两个序列的中位数大小,如果两个数相等,则该数为整个2n个数据的中位数,否则通过比较,分别减少两个序列的查找范围,确定查找的起止位置,继续查找。
思路
比较这两个序列的中位数大小,假定第一列的中位数为a,第二列的中位数为b。比较a和b:
(1)a<b:说明第一序列a前面的数太小了,第二序列b后的数太大了;
(2)a==b:两序列的中位数相等,不管其他数怎么排列,中位数一定是该数;
(3)a>b:说明第一序列a后面的数太大了,第二数列b前面的数太小了。
不断缩减两个序列的长度,直到两个序列都只剩下一个数时,由题意,选择的不是两数的平均值,而是较小的数。
代码实现
第一次的错误代码:
public class 寻找中位数 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入各数组包含的元素个数:");
int n = sc.nextInt();
int[] nums1 = new int[n];
int[] nums2 = new int[n];
System.out.println("请输入第一组元素:");
for(int i=0;i<n;i++) {
nums1[i]=sc.nextInt();
}
System.out.println("请输入第二组元素:");
for(int i=0;i<n;i++) {
nums2[i]=sc.nextInt();
}
sc.close();
System.out.println("这两组元素的中位数为:"+getNum(nums1,0,n-1,nums2,0,n-1));
}
private static int getNum(int[] nums1,int left1,int right1,int[] nums2,int left2,int right2) {
int mid1=(left1+right1)/2;
int mid2=(left2+right2)/2;
if(left1==right1&&left2==right2) {
return Math.min(nums1[mid1], nums2[mid2]);
}
if(nums1[mid1]==nums2[mid2]) {
return nums1[mid1];
}else {
if(nums1[mid1]>nums2[mid2]) {
return getNum(nums1,left1,mid1,nums2,mid2,right2);
}else {
return getNum(nums1,mid1,right1,nums2,left2,mid2);
}
}
}
}
调试:输入示例的数据后,初始情况如下:
不断调试,发现程序停留在:
找到原因: nums1数组经过一次比较后,left=0,right=1,mid=0,在经历第二次比较时,nums1[mid1](5)<nums2[mid2](14),但是由于nums1的mid前面并无数字了,而我又没有对此做处理,导致nums1一直停留在那里。
修改:
import java.util.Scanner;
public class 寻找中位数 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入各数组包含的元素个数:");
int n = sc.nextInt();
int[] nums1 = new int[n];
int[] nums2 = new int[n];
System.out.println("请输入第一组元素:");
for(int i=0;i<n;i++) {
nums1[i]=sc.nextInt();
}
System.out.println("请输入第二组元素:");
for(int i=0;i<n;i++) {
nums2[i]=sc.nextInt();
}
sc.close();
System.out.println("这两组元素的中位数为:"+getNum(nums1,0,n-1,nums2,0,n-1));
}
private static int getNum(int[] nums1,int left1,int right1,int[] nums2,int left2,int right2) {
int mid1=(left1+right1)/2;
int mid2=(left2+right2)/2;
if(left1==right1&&left2==right2) {
return Math.min(nums1[mid1], nums2[mid2]);
}
if(nums1[mid1]==nums2[mid2]) {
return nums1[mid1];
}else {
if(nums1[mid1]>nums2[mid2]) {
return getNum(nums1,left1,mid1==right1?mid1-1:mid1,nums2,mid2==left2?mid2+1:mid2,right2);
}else {
return getNum(nums1,mid1==left1?mid1+1:mid1,right1,nums2,left2,mid2==right2?right2-1:mid2);
}
}
}
}
代码试验成功。
分析
每个序列的比较数都减半,且该分治没有组合子问题的解的过程,可知递归方程:
T(n)=T(n/2)
时间复杂度为O(logn)。
新手写文章,如有误,敬请斧正。