【问题标题】:In an unsorted array, replace every element by the first larger element to the right在未排序的数组中,将每个元素替换为右侧第一个较大的元素
【发布时间】:2014-04-09 14:13:20
【问题描述】:

在未排序的数组中,我们必须将每个元素替换为右侧第一个大于当前元素的元素。如果右边没有一个元素更大,则应将其替换为-1

例子:

3  1  2  5  9  4  8   should be converted to
5  2  5  9 -1  8 -1

我可以想到一个简单的解决方案,我们用整个数组检查每个元素,这是一个 Ο(n²) 解决方案。有没有更好的方法来做到这一点?

【问题讨论】:

  • 不应该是-1 2 5 9 -1 8 -1吗?
  • @lakesh:据我了解,5 是第一个大于 3 的值,位于第一个位置的右侧
  • @NiklasB。哦,好的。错误地理解了这个问题。
  • 请注意,在最坏的情况下,平凡的算法只有O(n^2)。平均应该是O(n)
  • @nwellnhof:真的吗?那个怎么样?这对我来说不是很明显

标签: arrays algorithm data-structures time-complexity


【解决方案1】:

主要思想是以相反的顺序(从右到左)处理数组。我们提出几点意见:

  • 如果我们当前正在处理索引ik>j>iA[k] ≤ A[j],那么我们称元素k无关,因为它永远不会是任何元素 1, 2, ..., k 的结果
  • 因此,索引 i 右侧的相关元素形成了 A[i+1..n-1] 的单调严格递增子序列。

在您的示例中,相关元素的序列将从右到左:

       []    
      [8]
    [4,8]
      [9]
    [5,9]
  [2,5,9]
  [1,5,9]

它看起来像一个堆栈,我们确实可以使用堆栈来维护迭代之间的这个顺序。

在处理一个新元素时,我们首先需要找到数组元素的结果。观察结果是堆栈上的最顶部元素没有被新元素无效。因此,我们可以从堆栈中弹出所有变得无关紧要的元素。然后顶部是我们的结果。然后我们可以推送新元素,因为它与我们的定义相关。

stack = []
A = [3, 1, 2, 5, 9, 4, 8]
result = [-1]*len(A)
for i := len(A) - 1 to 0:
    # remove all elements made irrelevant by A[i]
    while not stack.empty() && stack.top() <= A[i]:
        stack.pop()
    # now the top of the stack is the result for index i
    if not stack.empty():
        R[i] = stack.top()
    # push the new element on the stack. The stack will still contain all relevant 
    # elements in increasing order from top to bottom
    stack.push(A[i])

迭代i 的循环不变量是“stack 包含索引i 右侧的相关元素的子序列”。很容易验证并暗示了该算法的正确性。

每个元素最多被推送和弹出一次,因此我们的总运行时间为 Ο(n)

【讨论】:

  • 我想你的意思是A[k] ≤ A[j]。另外,我认为如果您将ifwhile 的正文单独放在一行上,您的代码会更具可读性。除了细节:+1
  • 投反对票的人能给出理由吗?注意:我与此答案的作者无关
  • @WalterTross:是的,你是对的,改变了它。我还添加了一些语法高亮 :)
  • 算法和我完全一样,除了从右到左的过程。您可以在我的帖子中将其作为评论说。这不公平。
  • @Trying:首先,从概念上讲,它几乎是另一回事,而且更容易实现。其次,我试图添加一个实际的解释和理由,说明为什么这样做,以及为什么它具有预期的运行时间。我觉得它以多种方式增加了目的。您也不能声称对此背后的想法拥有所有权,因为这很明显,正如其他人所评论的那样
【解决方案2】:

可以使用栈,时间复杂度为O(N)

algo: 从左侧开始并向右移动。当您从数组中选择一个元素(比如说 x)时,弹出堆栈,直到堆栈中的元素(比如说 y)的元素大于数组元素,即 x> y。比将元素(即 x)推入堆栈。

例如{40,50,11,32,55,68,75} 。这里s 是堆栈。

40,因为s是空的推吧。 s: 40

50,因为 s.peek() s: 50

40 - 50 的下一个更高的元素。

11, s.peek() > 11 所以推 11。s: 50, 11

32, s.peek() s: 50 ,32

11 - 32 的下一个更高的元素。

55, s.peek() s: 55。

32 - 55 的下一个更高的元素。

50 - 55 的下一个更高的元素。

68, s.peek() s: 68

75, s.peek() s:75。

68 - 75 的下一个更高的元素。

由于数组没有任何元素,所以不只是弹出堆栈说对于数组内的所有元素都没有更大的元素,即-1。

75 的下一个更高的元素 - -1。

代码中的相同算法:

public static void fun(int[] a) {
    Stack<Integer> s = new Stack<Integer>();
    s.push(a[0]);

    for (int i = 1; i < a.length; i++) {
        if (s.peek() != null) {
            while (true) {
                if (s.peek() == null || s.peek() > a[i]) {
                    break;
                }
                System.out.println(s.pop() + ":" + a[i]);
            }
        }
        s.push(a[i]);
    }
    while (s.peek() != null) {
        System.out.println(s.pop() + ":" + -1);
    }
}

【讨论】:

  • 你如何处理非-1 元素?如何找到下一个更大的值?
  • 我认为您对此考虑得还不够充分。你能给出这个算法的(甚至是非常粗略的)伪代码吗?
  • 虽然我认为你的方法是正确的。它确实可以在O(n) 中工作。但是,您的实现是错误的
  • 虽然你的实现有小错误,但算法是正确的。一看到问题,我就在想 Niklas B 的方法,没想到从左到右的循环也行得通!
  • 没有投反对票,但我想这是因为您的 Java 示例不起作用
猜你喜欢
  • 1970-01-01
  • 2022-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多