对于整数数组,整数元素的超越是右侧上比它大的元素。
例如,{10,3,4,5,2}
,3
的超越者为4
和5
,10
没有任何超越者。
所以最大超越数问题是
给定一个整数数组
输出最大数量的超越者
基本上,我们需要获得每个元素的超越者数量,最后输出最大值。
例如,
{10,3,4,5,2}
的超越者的最大数量为 2 ,因为3
有2个超越者。
我想知道是否存在使用二进制搜索的O(nLogn)
解决方案。
我得到了这个问题,因为书中Pearls of Functional Algorithm Design的第2章说:
在这颗珍珠中,我们解决了Martin Rem的一个小编程练习 (1988a)。 虽然Rem的解决方案使用二进制搜索,但我们的解决方案是 另一种分而治之的应用。
虽然我得到了功能解决方案,但我不能想到一个二进制搜索数组。
有什么想法吗?
答案 0 :(得分:6)
我不知道这与Rem的解决方案是否相同,但您可以使用二叉搜索树轻松解决这个问题。
初始化一个空的二进制搜索树,然后以相反的顺序迭代通过数组。
在二叉搜索树中插入每个元素,然后对元素值上方的树中的所有元素执行计数。 (如果每个子树存储它包含的元素数,那么如果使用适当的平衡树,则这些操作都可以在O(logn)中完成。)
最大继承人数由观察到的最大数量给出。
具有类似基本思想的潜在更快的解决方案是使用二进制索引树(也称为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);
}
}
}