Max Max of Surpassers的二进制搜索解决方案

时间:2015-01-07 15:51:59

标签: algorithm binary-search

对于整数数组,整数元素的超越右侧上比它大的元素。

例如,{10,3,4,5,2}3的超越者为4510没有任何超越者。

所以最大超越数问题是

  

给定一个整数数组

     

输出最大数量的超越者

基本上,我们需要获得每个元素的超越者数量,最后输出最大值。

例如,

{10,3,4,5,2}的超越者的最大数量为 2 ,因为3有2个超越者。


我想知道是否存在使用二进制搜索O(nLogn)解决方案。

我得到了这个问题,因为书中Pearls of Functional Algorithm Design的第2章说:

  

在这颗珍珠中,我们解决了Martin Rem的一个小编程练习   (1988a)。 虽然Rem的解决方案使用二进制搜索,但我们的解决方案是   另一种分而治之的应用。

虽然我得到了功能解决方案,但我不能想到一个二进制搜索数组。

有什么想法吗?

2 个答案:

答案 0 :(得分:6)

解决方案1 ​​

我不知道这与Rem的解决方案是否相同,但您可以使用二叉搜索树轻松解决这个问题。

初始化一个空的二进制搜索树,然后以相反的顺序迭代通过数组。

在二叉搜索树中插入每个元素,然后对元素值上方的树中的所有元素执行计数。 (如果每个子树存储它包含的元素数,那么如果使用适当的平衡树,则这些操作都可以在O(logn)中完成。)

最大继承人数由观察到的最大数量给出。

解决方案2

具有类似基本思想的潜在更快的解决方案是使用二进制索引树(也称为Fenwick树)来存储元素。可以访问此数据结构以检索高于给定值的元素数,因此可以与解决方案1中的二叉搜索树相同的方式使用。

这将具有与解决方案1相同的O(nlogn)复杂度,但在实践中可能更快,因为它具有更小的内存占用。

答案 1 :(得分:1)

这是一个使用二进制搜索的解决方案,但我并不认为它是您正在寻找的(二进制搜索不是算法中使用的主要技术)。此解决方案的大部分工作都是通过排序和Fenwick树的组合完成的。二进制搜索组件可能可以由哈希映射或二叉搜索树替换。

无论如何,这是代码。如果您有任何问题,请与我联系:

import java.util.Arrays;

public class MaxSurpassersFenwick {
    public static void main(String[] args) {
        // The number of surpassers for a value is the number of values to the right and bigger.
        // The number of surpassers for { 2, 7, 5, 5, 2, 7, 0, 8, 1 } are { 5, 1, 2, 2, 2, 1, 2, 0, 0 }.
        // The max number of surpassers is thus 5.
        // The code I have written runs in O(n lg n) time.

        int[] values = new int[] { 2, 7, 5, 5, 2, 7, 0, 8, 1 };

        System.out.println("The max number of surpassers for any value in " + Arrays.toString(values) + " is " + getMaxNumSurpassers(values) + ".");
    }

    public static int getMaxNumSurpassers(int[] values) {
        if (values == null) {
            throw new IllegalArgumentException("Array of values cannot be null!");
        }

        int n = values.length;

        if (n <= 1) {
            return 0;
        }

        int[] numSurpassers = getNumSurpassers(values);
        int maxNumSurpassers = numSurpassers[0];

        for (int i = 1; i < n; ++i) {
            maxNumSurpassers = Math.max(maxNumSurpassers, numSurpassers[i]);
        }

        return maxNumSurpassers;
    }

    public static int[] getNumSurpassers(int[] values) {
        if (values == null) {
            throw new IllegalArgumentException("Array of values cannot be null!");
        }

        int n = values.length;
        int[] sortedValues = values.clone();
        FenwickTree numValues = new FenwickTree(n);
        int[] numSurpassers = new int[n];

        Arrays.sort(sortedValues);

        // Iterate through the values from right to left
        for (int i = n - 1; i >= 0; --i) {
            // Find the index of the current value in the sorted array of values
            // If the value occurs more than once, return the last index for that value
            int lastOccurrenceIndex = binarySearchLastOccurrence(sortedValues, values[i]);

            // Count the number of values we've seen that are greater than the current value
            numSurpassers[i] = numValues.sum(lastOccurrenceIndex + 1, n - 1);

            // Mark the current value as seen
            numValues.add(lastOccurrenceIndex, 1);
        }

        return numSurpassers;
    }

    private static int binarySearchLastOccurrence(int[] values, int valueToFind) {
        int low = 0;
        int high = values.length - 1;

        while (low <= high) {
            int mid = low + (high - low) / 2;

            if (mid == high || (values[mid] == valueToFind && values[mid] < values[mid + 1])) {
                return mid;
            } else if (values[mid] > valueToFind) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }

        return -1;
    }

    private static class FenwickTree {
        private int size;
        private int[] values;

        public FenwickTree(int size) {
            this.size = size;
            this.values = new int[size];
        }

        public void add(int index, int value) {
            while (index < this.size) {
                this.values[index] += value;
                index |= index + 1;
            }
        }

        public int prefixSum(int index) {
            int sum = 0;
            int n = index + 1;

            while (n > 0) {
                sum += this.values[n - 1];
                n &= n - 1;
            }

            return sum;
        }

        public int sum(int leftIndex, int rightIndex) {
            if (leftIndex > rightIndex) {
                return 0;
            }

            int rightSum = this.prefixSum(rightIndex);
            int leftSum = (leftIndex > 0) ? this.prefixSum(leftIndex - 1) : 0;

            return (rightSum - leftSum);
        }
    }
}