使用只读堆栈排序


14

请考虑以下设置:

  • 我们给出一个堆栈包含ñ项目。sñ
  • 我们可以使用常数的额外堆栈。Ø1个
  • 我们可以在这些堆栈上应用以下操作:
    1. 检查堆栈是否为空,
    2. 比较两个堆栈的顶部项,
    3. 删除堆栈中的第一项,
    4. 打印堆栈中的顶部项目,
    5. 将堆栈的顶层项目复制到另一个堆栈中,
    6. 将一个堆栈的内容复制到另一个堆栈。

请注意,这些是唯一允许的操作。我们不能交换项目,也不允许将任何项目推入任何堆栈中,除非将最上面的项目复制到堆栈中(之后,目标堆栈的先前内容将被丢弃,并且仅包含复制的项目) 。

这是一种使用比较对堆栈进行排序的算法:Øñ2

last := empty
for i from 1 to n
  min := empty
  w := s
  while w is not empty
    if w.top > last and w.top < min
      min := w.top
    delete w.top
  print min
  last := min

我们可以做得更好吗?

是否有一个程序仅使用比较来打印堆栈中项目的排序列表?Øñ日志ñ


2
听起来像寄存器的行为像堆栈吗?听起来您正在谈论推送和弹出操作。那是你的问题吗?您将如何通过使用多个堆栈和堆栈操作对堆栈进行排序?
AturSams

2
使用寄存器,您可以:只需将每个数字放入一个寄存器(O n ),然后应用常规的排序算法(O n lg n )。ñØñØñlgñ
卡夫2014年

1
您是否要使用寄存器?否则,问题就变得微不足道了,正如Kaveh所说。Ø1个
Yuval Filmus 2014年

1
别客气。我以为我们得到了很多堆栈,而不仅仅是一个堆栈,我会修复它。
卡夫

2
@akappa,您确定可以在这种情况下使用它吗?我们不能使大小大于1的任意丢失保持不变。您是否不需要存储排序的块?
卡夫

Answers:


1

我想我现在可以证明一个不重要的下限。这个想法是用一系列比较分支程序来实现任何这样的程序。“只读”假设意味着我们的分支程序家族使用的空间很少,即空间。然后我们应用由Borodin等人证明的下限S T = Ω n 2。在“在非遗忘的机器上进行排序的时空折衷”中。这给我们一个n 2 / log nO(logn)ST=Ω(n2)n2/logn下界。

更详细一点:我们可以省去上面的操作5。宽松地说,如果我们已经可以比较两个列表的开头并打印一个列表的开头,那么就没有必要在特定寄存器上隔离列表的开头。假设这样做,我们看到机器中的每个寄存器仅存储输入的最后一个子串。

假设我们的寄存器程序有行代码和k个寄存器X 1X kkX1,,Xk

修复。我们为长度为n的字符串构造比较分支程序,如下所示。创建用于每个元组的一个节点d 1... d ķ其中1 0 d 1... d ķÑ。这个想法是,寄存器机中的计算与分支程序中的路径相对应,并且我们位于节点i d 1dnn(i,d1,,dk)1i0d1,,dkn如果我们在套机的第 i行,并且存储在 X i中的字符串的长度是 d i(i,d1,,dk)iXidi。现在,我们必须定义分支程序的有向边

如果i的格式为

如果则转到i 1否则转到i 2Xu<Xvi1i2

然后,对于所有,通过比较输入的d u -th和d v -th元素来标记节点i d 1d k,并使“真”边变为i 1d 1d ki 2d 1d的“假”边缘d1,,dk(i,d1,,dk)dudv(i1,d1,,dk)(i2,d1,,dk)

如果第行的格式为i

,转到第 i 'X1tail(X2)i

那么从任何节点i 'd 21 d k都有一个箭头。(i,d1,,dk)(i,d21,,dk)

如果第行的格式为i

print(head(Xu))i

(i,d1,,dk)(i,d1,,dk)dü

ñķØ日志ñ


0

您能计算元素吗?然后,我认为有一个相当简单的Mergesort实现。如果您能够在堆栈上放置其他符号,则可以使用3个堆栈来解决它,如下所示:

If we have only one element, the list is already sorted. Now assume we have already sorted the top half of the stack. We can copy the top half (in reverse order) to the second stack and place a separation symbol on top of it. Now we have again 3 Stacks (since we can ignore the already sorted symbols below the separation symbol) and can sort the lower half. Finally we can copy the sorted lower half to a third stack in reverse order and merge both halfs back to the original stack.

All operations cost linear time, therefore we have sorted the list in O(nlogn)

(Note, that we need stacks of size nlogn because of the separation symbols, but this can be easily corrected by using another stack)


Since you can't put new elements on the stack you may get problems at the separation points. To solve this you may do the following with some additional stacks:

Copy the top n2logn elements to an additional stack, then proceed with the remaining elements as before. Now you know exactly the number of elements you need to consider in each step and therefore don't need any separation symbols.

Finally repeat the procedure with the additional elements at most logn times and merge them to the original stack in linear time. Sorting the stacks costs, for some constant c, at most cnlogn+cn2logn2+cn4logn4+ ...=O(nlogn) time, while merging costs an additional O(nlogn).


I'm not sure I understand. How can we, for example, copy the top half on the stack in reverse order onto another stack when we can never push any element onto any stack?
Siddharth

We can not push any new element to a stack, but according to 5 we are able to push the top element of one stack to another. So copying the stack in reverse order requires at most linear time. So I suppose, that was not what you were asking for?
cero

You can't push anything on top of other items as explained in the question.
Kaveh
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.