poj3579 二分搜索+二分查找

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 = 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;

  }

}

题目来源:http://poj.org/problem?id=3579

参考:https://www.cnblogs.com/xuejianye/p/5525747.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值