【问题标题】:deleting the element that is smaller than left side element in array删除数组中小于左侧元素的元素
【发布时间】:2021-12-06 15:54:25
【问题描述】:

我正在尝试编写一个程序,其输入是一个整数数组及其大小。此代码必须删除小于左侧元素的元素。我们希望找到重复此操作以无法删除更多元素的次数。这是我的代码,它可以工作,但我希望它更快。

你有什么想法可以让这段代码更快,或者其他比这更快的方法吗?

例如,给定数组 [10, 9, 7, 8, 6, 5, 3, 4, 2, 1],函数应该返回 2,因为 [10,9,7,8,6,5, 3,4,2,1] → [10,8,4] → [10]

int numberOfTimes(int array[] , int n) {
    int count = 0;
    int flag = 0;
    int sizeCounter = 0;
    while (true){
        for (int i = 0; i < n-1; ++i) {
            if (array[i]<= array[i+1]){
                sizeCounter++;
                array[sizeCounter] = array[i+1];
            } else{
                flag = 1;
            }
        }
        if (flag == 0)
            return count;

        count++;
        flag = 0;
        n = (sizeCounter+1);
        sizeCounter = 0;
    }
}

【问题讨论】:

  • 如果您不需要返回计数,那么可以通过保持内存中看到的最大元素一次性完成。但是我很难找到一种以这种方式“猜测”计数的方法。虽然这确实让人想起一个有巧妙解决方案的经典问题,但如果我能再次找到它的话。
  • 我没有完整的解决方案,但我会从识别峰、谷、下坡和上坡开始。例如,在数组 [10,5,3,4,6,8,7,1] 中,波峰为 10 和 8,波谷为 3 和 1,下坡为 10,5,38,7,1,以及上坡是3,4,6,8。一次通过即可消除下坡。上坡以每次 1 个元素的速度被移除。所以这个例子的计数是 4,因为最长的上坡有四个元素,峰 8 小于峰 10。
  • @wLui155 但是这个问题并不等价。考虑这个例子:array=[10,8,7,6,9] → [10,9] → [10]。答案是 2。但对于 i=4,我们得到 j=0i-j = 4.
  • @Stef 不错。从这个意义上说,我对“距离”的定义并不完全正确。它应该与删除元素所需的轮数同义。事实证明,正确的“距离”定义是递归的,这让事情变得有点棘手:distance[i] = max(distance[j]) + 1,对于每个 j &lt; i,如果是 array[j] &lt; array[i]。如果不存在这样的j,则distance[i] = 0。幸运的是,这个问题在线性时间内也是可以解决的,确实满足 10^5 的大小约束。
  • 我基本上声称删除 array[i] 所需的轮数是 1.0array[i] 未删除)2.1(它的邻居 array[i - 1] &gt; array[i] ) 或 3. 找到最左边大于array[i],array[k] 的元素。那么删除的次数是比删除索引ki 之间的一些中间元素的最大轮数多一倍。如上所述,问题看起来需要 O(N^2) 时间来解决,但通过一些巧妙的操作可以更快地完成。

标签: arrays c algorithm performance


【解决方案1】:

这个问题可以使用“单调堆栈”在 O(n) 时间和 O(n) 空间内解决。

对于array 的每个元素,我们将找到删除该元素所需的“动作/回合”数。换句话说,要经过多少圈,才能删除当前元素(包括)和最左边最近的较大元素之间的所有元素。

如果我们知道这个数字(我们称之为turns),那么我们可以找到数组中所有元素的最大值,并且我们将知道从数组中删除所有元素所需的轮数被移除。我们会有答案的。

现在,我们如何找到 turns 的值?这很容易,如果我们为当前元素左侧的所有元素计算了这些turns 值。我们只找到大于当前元素的最接近的元素,并为该元素和当前元素之间的每个元素找到turns 的最大数量。 IE。如果当前元素在索引i,并且最近的更大元素在索引jj &lt; iarray[j] &gt; array[i]),turns[i] = max(turns[k]+1), for k in [j+1..i-1]

如果我们天真地这样做,为每个元素找到turns 将是 O(n)。幸运的是,很容易看出,当我们为某些i 找到j 时,我们将不再需要考虑ji 之间的元素。请记住,array[j] &gt; array[i] 以及介于ji 之间的所有内容都小于array[i]。我们正在寻找大于某个值的最接近的数组元素,因此,如果array[i] 不是答案,则整个[j+1..i-1] 范围也不是答案,我们可以直接转到j

考虑到这一点,我们来到了单调堆栈。我们不是为array 的每个元素存储turns,而是只为我们迄今为止访问过的array 的严格递减子序列存储它。

在将新元素添加到堆栈之前,首先我们需要删除每个小于当前元素的元素。那么栈顶就是我们的array[j]

由于每个元素都被添加到堆栈中并仅删除一次,因此为每个元素找到turns 的摊销成本为 O(1),因此整个算法为 O(n)。在最坏的情况下,堆栈的大小会增长到与数组相同的大小(如果array 严格减少)。所以空间复杂度是O(n)。

这里是代码(python):


array = [10, 9, 7, 8, 6, 5, 3, 4, 2, 1]
s = [] # monotonic stack of pairs (array[j],turns[j])
count = 0 # result: number of turns to delete all deletable elements

for el in array:
  # initially assuming current element can be deleted
  turns = 1 
  
  # peeking at the top of the stack
  while len(s) > 0 and s[-1][0] <= el:
    _,t = s.pop()
    turns = max(t+1, turns)

  # corner case: current element is the largest so far, cant be deleted
  if len(s) == 0:
    turns = 0
  s.append( (el, turns) )
  count = max(count, turns)

print(count)

测试:

[10, 9, 7, 8, 6, 5, 3, 4, 2, 1] → 2
[10, 9, 7, 8, 6, 5, 3, 4, 2, 1, 9] → 3
[10, 9, 7, 8, 6, 5, 3, 4, 2, 1, 11] → 2
[] → 0
[1, 2, 3] → 0
[1, 2, 3, 1] → 1
[10, 1, 2, 3, 4, 5] → 5

UPD:我刚刚阅读了 cmets,我想向@wLui155 表示敬意,他提出了与我之前相同的核心想法。

【讨论】:

    猜你喜欢
    • 2014-11-08
    • 2020-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多