在列表中找到比当前元素大 n 倍的第一个元素

Find the first element that is n times larger than current element in a list

想出一个 O(n) 算法来解决这个非常著名的问题很容易:

For every element in the list, find the first element that is larger than it. This can be done using a stack. But, what if I want to find the first element that is larger than n*current element?

更具体地说:

给定一个数组 [2, 5, 4, 7, 3, 8, 9, 6] 并且 n = 2。

我要 [5, -1, 9, -1, 8, -1, -1, -1] 对于2,5是下一个大于n * 2的元素,对于4,9是下一个大于n * 4的元素。对于5,没有元素大于n * 5所以return -1位置。

我们能比 O(n^2) 做得更好吗?

当然,很容易。

我们只需要一个排序版本的数组(排序后的元素加上它们的原始索引),然后我们就可以进行有效的搜索(修改后的二分搜索),将我们指向大于元素的开始当前数字(或者它的倍数,没关系)。然后我们可以按顺序搜索那些具有最小索引的元素(如果需要,它大于当前数字之一)。

编辑:有人指出算法不一定比O(n²)好,因为顺序搜索满足大于条件的元素。可能是这样,我不确定。 但请注意,我们可能会构建一个更复杂的搜索结构,以某种方式已经涉及索引。这是留作作业。 :)

我同意 OP 的观点,即在剩余数组中查找 > 2x 的第一个元素时,O(N) 算法的简单谓词可能不适用于基于堆栈的解决方案。

顺便说一句,我找到了一个复杂度为 O(NlogN) 的解决方案。

它使用了一个Min-heap来维护我们感兴趣的前沿元素

伪代码:

def get_2x_elements(input_list, multipler = 2):
  H = [] #min-heap with node-values as tuples (index, value)
  R = [-1 for _ in range(len(input_list))] # results-list

  for index, value in enumerate(input_list):
    while multiplier*H[0][1] < value:
      minval = extractMinFromHeap(H)
      R[minval[0]] = value

  insertToMinHeap(H, (index, value))

  return R

复杂性分析:

1. Insertion/Extraction from min-heap = O(logN)
2. Number of such operations = N

Total-complexity = O(NlogN)

PS:假设我们需要列表剩余部分的 first >2x element

回复: 我对你的想法进行了 Java verion 实现。谢谢@Serial Lazer


    private static class ValueAndIndexPair implements Comparable<ValueAndIndexPair>{
        public final double value;
        public final int index;

        public ValueAndIndexPair(double value, int index) {
            this.value = value;
            this.index = index;
        }

        @Override
        public int compareTo(ValueAndIndexPair other) {
            return Double.compare(value, other.value);
        }
    }

    public static double[] returnNextNTimeLargerElementIndex(final List<Double> valueList, double multiplier) {
        double[] result = new double[valueList.size()];
        PriorityQueue<ValueAndIndexPair> minHeap = new PriorityQueue<>();

        // Initialize O(n)
        for (int i = 0; i < valueList.size(); i++) {
            result[i] = -1.0;
        }
        if (valueList.size() <= 1) return result;

        minHeap.add(new ValueAndIndexPair(valueList.get(0) * multiplier, 0));

        for (int i = 1; i <valueList.size(); i++) {
            double currentElement = valueList.get(i);
            while (!minHeap.isEmpty() && minHeap.peek().value < currentElement) {
                result[minHeap.poll().index] = currentElement;
            }
            minHeap.add(new ValueAndIndexPair(currentElement * multiplier, i));
        }
        return result;
    }

基于堆栈的解决方案 offered at geeksforgeeks 似乎没有保持结果中元素的顺序,即使在其输出中也是如此:

input: int arr[] = { 11, 13, 21, 3 };
output: 
 11 -- 13
 13 -- 21
 3 -- -1
 21 -- -1

在找到比电流大 N 倍的第一个元素后,该算法在给定示例中无法检测到元素 4 的 9。

Online demo

input: int arr[] = { 2, 5, 4, 7, 3, 8, 9, 6 }; // as in this question
output: 
2 * 2 --> 5
2 * 3 --> 8
6 -- -1
9 -- -1
8 -- -1
7 -- -1
4 -- -1
5 -- -1

因此,关于复杂度为 O(N) 的现有解决方案的初始假设并不完全适用于预期结果。