使用二进制搜索来计算小于/大于给定数字的元素数量

时间:2018-02-27 11:44:09

标签: java

给定数字current,找到数组中大于和小于该值的值的数量。

//sort array for binary search
 int[] digits = Arrays.stream(sc.nextLine()
            .split(" "))
            .mapToInt(Integer::parseInt)
            .sorted()
            .toArray();

//for duplicate values, find higher index of current.

   while(low <= high){
        int mid = low + (high - low)/2;
        if(digits[mid] > current){
            high = mid - 1;
        }else if (digits[mid] == current){
            startindex = mid;
            high = mid - 1;   
        }else{
            startindex = mid;
            low = mid +1;
        }
    }

//for duplicate values, find lower index of current.
    int endindex = -1;
    low = 0;
    high = no_digits - 1;

    while(low <= high){
        int mid = low + (high - low)/2;
        if(digits[mid] > current){
            high = mid - 1;
        }else if (digits[mid] == current){
            endindex = mid;
            low = mid + 1;   
        }else{
            endindex = mid;
            low = mid + 1;
        }
    }

    System.out.println(endindex + "-" + startindex);

    if(digits[0] > current){
        smallest = 0;
        largest = no_digits;
        System.out.println(String.format("Smaller: %d, Greater: %d", smallest, largest));
    } else if (digits[no_digits - 1] < current){
        smallest = no_digits;
        largest = 0;
        System.out.println(String.format("Smaller: %d, Greater: %d", smallest, largest));
    }else {
        smallest = startindex;
        largest = no_digits - endindex - 1;                
        System.out.println(String.format("Smaller: %d, Greater: %d", smallest, largest));
    }
}

}

示例输入:

5 8 7 2 4 3 7 9 1 9 - Array of ints.

7
0
100
3
6

输出:

Smaller: 5, Greater: 3
Smaller: 0, Greater: 10
Smaller: 10, Greater: 0
Smaller: 2, Greater: 7
Smaller: 5, Greater: 5

我的结果:

6-5 //start and end index.
Smaller: 5, Greater: 3
-1--1 
Smaller: 0, Greater: 10
9-9
Smaller: 10, Greater: 0
2-2
Smaller: 2, Greater: 7
4-4
Smaller: 5, Greater: 4

我设法推出了上述算法,该算法考虑的值大于或低于数组中的任何值。

但是,由于我需要在O((N + Q)log N)时间内完成上述操作,因此我无法找到解决数组中不存在的值而无需迭代数组的解决方案。

在这种情况下,这将是值为6的最后一个测试用例。数组中不存在6,但我仍然需要计算高于/低于6的所有值。

6 个答案:

答案 0 :(得分:3)

二进制搜索算法为数组中不存在的值生成“插入点”。您的startIndexendIndex会为您提供第一个“符合条件”的项目,或者旁边的项目。换句话说,如果您要查找小于6的所有值,搜索端点将产生5的索引。

请注意,您不需要滚动自己的二进制搜索算法:Java为您提供了一个实现。

参考:Arrays.binarySearch

答案 1 :(得分:2)

编辑这个问题已被编辑,现在它还包含一个额外的要求,即算法应该能够快速地进行多个查询,更准确地说:整个运行时应该是O((N + Q) * log(N))其中{{1是数组的大小,N是查询的数量。

以下方法仅适用于Q

我没有看到任何理由不在线性Q = 1时间内这样做。

O(N)

输出:

// get this from scanner
int number = 5;
int[] array = {6, 2, 7, 4, 1, 42};

// the "algorithm"
int numLessThan = 0;
int numGreaterThan = 0;
for (int i: array) {
  if (i < number) numLessThan++;
  if (i > number) numGreaterThan++;
}
System.out.println(
  "Num greater than: " + numGreaterThan + " " +
  "Num less than: " + numLessThan
);

如果你坚持用溪流做这件事:

Num greater than: 3 Num less than: 3

即使它遍历数组两次,它仍然是long numLessThan = Arrays.stream(array).filter(x -> x < number).count(); long numGreaterThan = Arrays.stream(array).filter(x -> x > number).count();

答案 2 :(得分:2)

由于您使用ClientStream - 呼叫不少,您仍然会重复整个阵列。

所以就这样做

map

或更通用的版本

class Counters {
    AtomicInteger smaller = new AtomicInteger(0);
    AtomicInteger larger = new AtomicInteger(0);
    private final int upperLimit;
    private final int lowerLimit;
    public Counters(int up, int down) {
        upperLimit = up;
        lowerLimit = down;
    }
    public void consider(int value) {
        if (value > upperLimit) larger.incrementAndGet();
        if (value < lowerLimit) smaller.incrementAndGet();
    }
    public int getSmaller() { return smaller.get(); }
    public int getLarger() { return larger.get(); }
}

Counters c = new Counters(upper, lower);
IntStream.of(yourValues).parallel().forEach(c::consider);
// your output here
System.out.printf("Smaller: %d - Larger: %d", c.getSmaller(), c.getLarger());

答案 3 :(得分:1)

请参阅Arrays,这将会派上用场。

void stats(int[] a, int sought) {
    a = Arrays.copyOf(a, a.length);
    Arrays.sort(a);
    int index = Arrays.binarySearch(a, sought);
    int smaller, larger;
    if (index < 0) {
        // Not found.
        index = ~index; // Insertion position.
        smaller = index;
        larger = index:
    } else {
        // Found.
        smaller = index;
        while (smaller > 0 && a[smaller] == sought) {
            --smaller;
        }
        while (index <= 0 && a[index] == sought) {
            ++index;
        }
    }
    larger = a.length - index;
    int equals = index - smaller;
    System.out.printf("Smaller %d, equal %d, larger %d.%n",
            smaller, equals, larger); 
}

如您所见,找到一个元素时,回路O(N)就足够了,它小于排序O(N log N)。

更快 - O(log N)代替那个部分的O(N) - 如果有人可以对搜索的二进制搜索 - 0.5并且寻求+ 0.5。

void stats(int[] a, int sought) {
    a = Arrays.copyOf(a, a.length);
    for (int i = 0; i < a.length; ++i) {
        a[i] *= 2;
    }
    Arrays.sort(a);
    int smallerI = Arrays.binarySearch(a, 2 * sought - 1);
    int largerI = Arrays.binarySearch(a, 2 * sought + 1);
    int smaller = ~smallerI;
    int larger = a.length - ~largerI;
    int equals = ~largerI - ~smallerI;
    System.out.printf("Smaller %d, equal %d, larger %d.%n",
            smaller, equals, larger); 
}

这使用了加倍的整数,其缺点是数组值的有效域减半。

在你的情况下,你自己的二元搜索算法应该选择后一种情况(不加倍),使用隐式sought + 0.5,永远不会找到,寻找插入位置。

答案 4 :(得分:1)

好的,所以在你编辑之后你说你要在同一个数组上运行几个查询,所以准备时间不那么重要。

为此,从数组中构建red-black tree;这将为您提供一个允许在O(log N)中搜索的排序结构。

那么你为&#34;较小的&#34;做了什么? count是向左移动,直到找到一个值等于或大于下限的节点;算上所有左边的孩子。模拟较大的(向右,找到相等或更小,向右计数)。

如果项目不在数组中,那将无关紧要,因为您正在寻找一个“等于或大于”的数据。如果是6不存在,但是你找到了5,你从那里开始计算 - 只有你在计数中加1。

答案 5 :(得分:0)

您只需要过滤然后计算出现次数。例如:

public static void main(String[] args) {
    int[] values = {5, 8, 7, 2, 4, 3, 7, 9, 1, 9};
    printCount(values, 7);
    printCount(values, 0);
    printCount(values, 100);
    printCount(values, 3);
    printCount(values, 6);

}
private static void printCount(int[] values, int value) {
    long smallerCount = Arrays.stream(values).filter(v -> v < value).count();
    long largerCount  = Arrays.stream(values).filter(v -> v > value).count();
    System.out.println(String.format("Smaller : %d, Larger: %d", smallerCount, largerCount));
}