【问题标题】:Check if two stacks are equal in O(1) space检查两个堆栈在 O(1) 空间中是否相等
【发布时间】:2020-01-16 09:54:59
【问题描述】:

我在一次采访中被问到这个问题:

使用 O(1) 空间检查给定的两个堆栈是否相等(元素的大小和顺序)。

堆栈应保持其早期状态(元素顺序)。

递归使用堆栈空间,因此无效。

我的一个朋友建议使用字符串变量并将一个堆栈中的元素附加到其中,然后将弹出的元素推送到另一个堆栈,然后从 S2(S1 的旧元素)推送到 S1,并对 S2 执行相同的操作。但这取决于堆栈的大小,因为字符串会根据堆栈的大小而增长。

【问题讨论】:

  • 仅仅从两个堆栈中弹出并比较弹出的项目直到其中一个堆栈为空是不够的?如果任何一对不相等,它们就不相等
  • 堆栈应保持其初始状态,但此处堆栈将处于不同状态。

标签: data-structures stack


【解决方案1】:

递归 vs 迭代不是这里的问题。问题是如何保持堆栈完好无损,因为完整检查堆栈的唯一方法是清空它。

因此,您需要将弹出的成员存储到临时堆栈中。这不会改变整体空间需求,因为您只推送到其他地方弹出的临时堆栈对象,因此您满足 O(1) 备用需求。

这是迭代解决方案的伪代码:

function stacks_are_equal
  precondition: stacks s1, s2
set equal? to true
while s1 is not empty and s2 is not empty pop x from s1 pop y from s2 push x onto temp1 push y onto temp2
if xy then set equal? to false break out of loop
if s1 is not empty or s2 is not empty set equal? to false
while temp1 is not empty pop x from temp1 pop y from temp2 push x onto s1 push y onto s2
return equal?

这是递归解决方案:

function stacks_are_equal(s1, s2)
  if s1 is empty and s2 is empty return true
  if s1 is empty or s2 is empty return false
pop x from s1 pop y from s2
empty? := x = y and stacks_are_equal(s1, s2)
push x onto s1 push y onto s2
return empty?

【讨论】:

  • 嘿,如果我们这样看,递归也应该是一个有效的解决方案,因为它将堆栈的值保留在递归堆栈中?
  • "...因为您只推送到其他地方弹出的临时堆栈对象,因此您满足 O(1) 空间要求。" - 也许。如果堆栈被实现为链表,那是真的。但是如果堆栈是作为一个数组实现的,那么从堆栈中弹出一个项目并不会减少堆栈使用的内存。递归版本不可能是 O(1) 空间。堆栈帧不是免费的。如果列表足够大,您将耗尽堆栈空间。
  • @JimMischel 让我分别解决这些问题,从最后一点开始:仅仅因为堆栈可能溢出,not 是否意味着算法不是 O(1)。出于渐近空间分析的目的,我们不假设任何限制(换句话说,我们假设无限资源)。堆栈空间在这方面并不特殊:如果您的输入数组太大而无法放入主内存,则就地排序算法(= O(1))同样可能会崩溃。而且您总是可以使调用堆栈更大。这些是物理限制,虽然它们在实践中是相关的,但对于渐近分析来说并不重要。
  • @JimMischel 您的第一点同样混淆了接口和实现。 “堆栈”是一种抽象数据类型。但是这个 ADT 的严格规范通常会指定堆栈需要 O(n) 中的渐近、摊销空间;也就是说,我们可以在渐近分析中忽略它的实际空间需求。 在实践中将数据存储在固定大小的数组中可能很方便只要我们知道实际使用量不会超过这个存储空间。如果是这样,我们就违反了 ADT 强加的合同。这不会使我们的渐近分析无效;相反,这是一个“错误”。
  • @JimMischel 与往常一样,渐近分析是一种(有意)忽略物理约束的理论分析。实际上,我们没有无限的资源,没有无限的图灵磁带。空间和运行时间分别受到宇宙中原子数量和熵的限制。就像我们无法绘制物理的、无限的数轴一样,我们也无法将无限的数据提供给 O(1) 算法。但这不会导致数学无穷大和渐近分析的概念无效。
【解决方案2】:

使用递归...这是一个使用 java 的示例。

private static boolean equalhelper(Stack<Integer> s1, Stack<Integer> s2)
{
    if(s1.isEmpty() && s2.isEmpty())
        return true;
    if(s1.isEmpty() || s2.isEmpty())
        return false;       

    Integer a=s1.pop();
    Integer b=s2.pop();
    if(!a.equals(b))
    {
        s1.push(a);
        s2.push(b);
        return false;
    }
    else
    {
        boolean check= equalhelper( s1, s2);
        s1.push(a);
        s2.push(b);
        return check;
    }
}

【讨论】:

  • 这个方法有效(几乎!——你需要用! a.equals(b)替换a != b)但是它做了很多多余的工作。如果您删除不必要的冗余,您可以将大小几乎减少一半。
  • 感谢您的更新。进行了一些编辑。实际上,Java 具有拆箱功能,这就是 a!=b 甚至可以工作的原因。但是删除了编辑和冗余代码
  • 拆箱是在欺骗你,因为相等比较执行拆箱!它测试常规引用相等性。它有时会起作用,因为 Java 缓存了一些值,但一般情况下它不会起作用。试试看:Integer a = 1; Integer b = new Integer(1); a.equals(b);——这将产生false!同样,Integer a = 325125; Integer b = 325125; a.equals(b); 也会产生 false
  • 使用两个相同的列表,每个列表包含 100 万个项目。您将收到堆栈溢出错误。所以这不可能是 O(1) 空间。
猜你喜欢
  • 2012-10-03
  • 2012-03-16
  • 2020-05-10
  • 2015-08-21
  • 1970-01-01
  • 2012-10-24
  • 2011-08-09
  • 2020-02-12
  • 2012-10-10
相关资源
最近更新 更多