Description
Given N numbers, X1, X2, ... , XN, let us calculate the difference of every pair of numbers: ∣Xi - Xj∣ (1 ≤ i < j ≤ N). We can get C(N,2) differences through this work, and now your task is to find the median of the differences as quickly as you can!
Note in this problem, the median is defined as the (m/2)-th smallest number if m,the amount of the differences, is even. For example, you have to find the third smallest one in the case of m = 6.
Input
The input consists of several test cases.
In each test case, N will be given in the first line. Then N numbers are given, representing X1, X2, ... , XN, ( Xi ≤ 1,000,000,000 3 ≤ N ≤ 1,00,000 )
Output
For each test case, output the median in a separate line.
Sample Input
4
1 3 2 4
3
1 10 2
Sample Output
1
8
分析:
题目大意:N个数字,任意两个数字之间的差共有m=(n-1)*n/2种,然后在输出这些数字中间的哪一个。规则为:若m为奇数,则中间的那一个为第(m-1)/2小的数字,若m为偶数,则中间的数字指的是第m/2小的数字。
思路:
1)先将a[n]排序。
2)x 为中位数 , |aj−ai|<x 的个数恰好为一半, 所有的aj>ai 中 aj<x+ai 个数为一半 。
x 从 [0,max{|aj−ai|}] 中二分查找
枚举i,在a[n]中利用uppperBound()二分查找x+ai<aj(aj>ai) 的个数,若恰为一半,则x为所求
代码如下:
public class BinarySearchTest {
private static int n = 0;
private static int m = 0;
private static int[] array;
private static int mid = 0;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入数字个数:");
n = scanner.nextInt();
scanner.nextLine();
System.out.println("请输入数字串(不同数字键空格间隔): ");
String[] strs = scanner.nextLine().trim().split(" ");
array = new int[n];
for (int i = 0; i < n; i++){
array[i] = Integer.parseInt(strs[i]);
}
scanner.close();
m = n*(n-1) >> 1;
mid = (m+1) >> 1;
Arrays.sort(array);
int l = 0;
int r = array[n-1] - array[0];
int ans = 0;
while (l <= r){
int middle = (l + r) >> 1;
if (check(middle)){
ans = middle;
r = middle -1;
} else {
l = middle + 1;
}
}
System.out.println(ans);
}
//统计比a[i]+d小的数有多少个,如果 >=(m+1)/2就return true else return false
public static boolean check(int x){
int cnt = 0;
for (int i = 0; i < n; i++){
//比a[i]+x小的元素的个数
int t = upperBound(array, 0, n, array[i] + x);
//排除a[i]之前的那些元素,因为i从0开始,所以共有i+1个
cnt += (t - i - 1);
}
//比a[i]+x大的元素个数若大于中位数,则返回true,同时也说明r应该缩小
if (cnt >= mid){
return true;
}
return false;
}
//在按照非递减顺序排好序的数组中找到第一个大于给定值target的那个数的坐标
public static int upperBound(int[] nums, int l, int r, int target) {
while (l < r) {
int m = (l + r) >> 1;
if (nums[m] <= target) {
l = m + 1;
} else {
r = m;
}
}
return l;
}
}