JAVA实现两个序列的中位数_求两个有序数组合并后的上中位数的非递归算法实现 - JAVA版...

packagetest;public classFindMedian {//求两个有序数组合并后的上中位数。折半方法(二分查找),时间复杂度为O(logN),其中N是小数组的长度//中位数特性:1、数组一半不超过该值,一半不小于该值;2、从首尾各删除相同个数元素,中位数不变

public static int findMedianNum(int[] arr1, int[] arr2) throwsRuntimeException{//判断存在空数组的情况,直接返回结果

if (arr1 == null && arr2 == null) throw new RuntimeException("null arr");//因为数组下标是从0开始,所以上中位数的下标为(startIndex + endIndex)/2,如一个长度为12的数组,中位数下标为(0+11)/2=5,即第六个元素

else if (arr1 == null || arr1.length == 0) return arr2[(arr2.length - 1)/2];else if (arr2 == null || arr2.length == 0) return arr1[(arr1.length -1 )/2];//将长度小的数组放在前面,便于判断和二分,因为每次两个数组都要减掉小数组长度的二分之一

int[] tempArr1 = arr1, tempArr2 =arr2;if (arr1.length > arr2.length) {tempArr1 = arr2; tempArr2 =arr1;}int length1 = tempArr1.length, length2 =tempArr2.length;int start1 = 0, end1 = length1 - 1, start2 = 0, end2 = length2 - 1;intmid1, mid2, subLength;while (start1 < end1) { //每次都会缩小数组比对的范围

mid1 = (start1 + end1)/2; mid2 = (start2 + end2)/2; //待比对数组范围的中位数下标

subLength = length1/2; //缩减的范围长度:小数组长度的二分之一

if (tempArr1[mid1] ==tempArr2[mid2]) {return tempArr1[mid1]; //如果两个数组的中位数相等,那么这个值也是整个合并后数组的中位数

} else if (tempArr1[mid1] < tempArr2[mid2]) { //说明合并后的中位数在tempArr1的后半部分或者tempArr2的前半部分//虽然可以确定在两个数组的哪半段,但是不能单纯各减一半,而是两个数组缩小相同的长度(小数组长度的一半),否则中位数的位置就发生偏移了。//注:是从中位数不在的那半边中减去对应长度。

start1 = start1 + subLength; end2 = end2 -subLength;

length1= length1 - subLength; length2 = length2 -subLength;

}else if (tempArr1[mid1] > tempArr2[mid2]) { //说明合并后的中位数在tempArr1的前半部分或者tempArr2的后半部分

end1 = end1 - subLength; start2 = start2 +subLength;

length1= length1 - subLength; length2 = length2 -subLength;

}

}//当start1=end1时,退出循环,即短的数组里只剩下了一个数。此时用这个数跟长数组剩余元素的中位数做比对,以确定最终中位数

mid2 = (start2 + end2)/2;if (length1 == length2) { //两个数组长度都剩1,元素值小的那个是前中位数

return tempArr1[start1] < tempArr2[start2] ?tempArr1[start1] : tempArr2[start2];

}else if (length2 % 2 == 0) {//如果长数组剩余长度为偶数,则合并后长度是奇数,所以应在tempArr1[start1]、tempArr2[mid2]、tempArr2[mid2+1]三个数中找中间值,其中后两者之间有序

if (tempArr1[start1] >= tempArr2[mid2+1]) return tempArr2[mid2+1];else if (tempArr1[start1] <= tempArr2[mid2]) returntempArr2[mid2];else returntempArr1[start1];

}else { //length2 % 2 == 1的时候//如果长数组剩余长度为奇数,则合并后长度是偶数,所以应在tempArr1[start1]、tempArr2[mid2-1]、tempArr2[mid2]三个数中找中间值,其中后两者之间有序

if (tempArr1[start1] >= tempArr2[mid2]) returntempArr2[mid2];if (tempArr1[start1] <= tempArr2[mid2-1]) return tempArr2[mid2-1];else returntempArr1[start1];

}

}//求两个有序数组合并后的上中位数。采用合并排序找第n小的方法,时间复杂度为O((length1 + length2)/2)

public static int findMedianNum2(int[] arr1, int[] arr2) throwsRuntimeException{//判断存在空数组的情况,直接返回结果

if (arr1 == null && arr2 == null) throw new RuntimeException("null arr");//因为数组下标是从0开始,所以上中位数的下标为(startIndex + endIndex)/2,如一个长度为12的数组,中位数下标为(0+11)/2=5,即第六个元素

else if (arr1 == null || arr1.length == 0) return arr2[(arr2.length - 1)/2];else if (arr2 == null || arr2.length == 0) return arr1[(arr1.length -1 )/2];int length1 = arr1.length, length2 =arr2.length;int[] conArr = new int[length1 + length2]; //合并后的数组容器,如果单纯求中位值,这个容器可有可无

int midIndex = (arr1.length + arr2.length - 1)/2; //因为index从0开始,所以分子需要减1

int i = 0, j = 0, k = 0; //i为arr1下标,j为arr2下标,k为合并数组下标

while (i < length1 && j

conArr[k] =arr1[i];if (k == midIndex) return arr1[i]; //k为本次遍历完成后,合并数组的下标

i++; k++; //下次遍历,合并数组和分数组的下标

} else { //如果有合并后数组容器,此时应将arr2[j]放入数组

conArr[k] =arr2[j];if (k == midIndex) returnarr2[j];

j++; k++;

}

}//如果其中一个数组已经遍历完还累计到midIndex,则继续遍历剩下的那个数组

if (i ==length1) {while (j

conArr[k]=arr1[i];if (k == midIndex) returnarr2[j];

j++; k++;

}

}else{while (i

conArr[k]=arr2[j];if (k == midIndex) returnarr1[i];

i++; k++;

}

}//按照逻辑,上面的循环内一定会方法返回。此处的return只是为了让代码能够编译通过,实际不会执行到。

return 0;

}public static voidmain(String[] args) {int[] arr1 = new int[] {0,1,2,3,4,7,10};int[] arr2 = new int[] {5,6,7,8,9,11};

System.out.println(FindMedian.findMedianNum(arr1, arr2));

System.out.println(FindMedian.findMedianNum2(arr1, arr2));

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值