我正在阅读an algorithm to get the Next Greater Element for each element of an array。
该网站声称他们的代码在O(n)
时间内运行,但我无法绕过它。
从左到右对数组的完整遍历本身将花费O(n)
,最重要的是,当我们遍历列表时,每个索引可以有多个推/弹操作。
虽然推/弹操作占用一定的时间,但如果每个索引元素平均调用m
次,那么我们将O(m)
作为每个索引的推/弹操作的成本导致O(mn)
作为总费用,现在因为推/弹操作的总数应该大约(或可能完全)等于n
,我们可以说mn
是大约(或可能完全)等于n
,暗示m
是常数。
我的理由是对的吗?
我仍然没有适当的清晰度,除了验证/无效我的理由之外,还有人可以提供更好的解释吗?
答案 0 :(得分:2)
为方便起见,这里复制了伪代码:
- 将第一个元素推到堆栈。
- 逐个选择其余元素,然后按循环执行以下步骤
- 将当前元素标记为 next 。
- 如果stack不为空,则从堆栈中弹出一个元素并将其与 next 进行比较。
- 如果next大于弹出元素,则 next 是弹出元素的下一个更大元素。
- 当弹出元素小于 next 时,继续从堆栈弹出。 next 成为所有此类弹出元素的下一个更大元素
- 如果 next 小于弹出元素,则将弹出的元素推回。
- 将 next 推入堆栈[伪代码中缺少此步骤]
- 在步骤2中的循环结束后,弹出堆栈中的所有元素并打印-1作为它们的下一个元素。
醇>
外部循环运行O(n)
次。
显然,除了没有被推回的弹出元素所做的工作(步骤#4,减去它处理的最后一个元素),循环迭代的其余部分是恒定时间。
因此,任何不断弹出并推回到堆栈的元素都将被包含在每次迭代的常量时间中,它会被弹出并推回。
剩下的就是弹出元素而不是推回元素的时间,但显然每个元素只能发生一次,因此这总共占O(n)
个运行时间。< / p>
由于我们从不复制堆栈中的项目(我们将数组中的每个元素推送一次,然后我们只在弹出后再次推送),因此不能超过n
个元素堆栈,所以最后一步最多需要O(n)
次。
因此总运行时间为O(n + n + n) = O(n)
。
另一种推理方式:
我们在每次循环迭代期间执行最多2次推送操作(有n
)。
因此,最多只有2n
个推送操作,因此最多只有2n
个弹出操作(我们无法弹出未被推送的元素)。
我们每次推送/弹出操作都会进行一定量的工作,而且我们还会在每次循环迭代中执行一定量的工作(忽略每次推送/弹出操作所完成的工作),因此总运行时间为{{1} }。
答案 1 :(得分:0)
m
每次迭代可能会n
次操作,但m
仍然是常量,与n
无关。
每次n
次迭代的推/弹操作次数都是不变的,因此它们不会影响算法的时间复杂度。