4

这是我在一本算法/面试题书上找到的,别人在这本上发了好几篇帖子,但我还是有一些疑问,以下是书中的原文和代码:

implement 3 stacks using 1 array:
Approach 2:

在这种方法中,只要数组中有任何可用空间,任何堆栈都可以增长。我们按顺序为堆栈分配空间,并将新块链接到前一个块。这意味着堆栈中的任何新元素都会保留指向该特定堆栈的前一个顶部元素的指针。在这个实现中,我们面临一个未使用空间的问题。例如,如果堆栈删除了它的一些元素,则删除的元素可能不一定出现在数组的末尾。因此,在这种情况下,我们将无法使用那些新释放的空间。为了克服这个缺陷,我们可以维护一个空闲列表,整个数组空间最初会被分配给空闲列表。对于每次插入,我们将从空闲列表中删除一个条目。在删除的情况下,我们只需将空闲单元格的索引添加到空闲列表中。

1 int stackSize = 300;
2 int indexUsed = 0;
3 int[] stackPointer = {-1,-1,-1};
4 StackNode[] buffer = new StackNode[stackSize * 3];

5 void push(int stackNum, int value) {
6     int lastIndex = stackPointer[stackNum];
7     stackPointer[stackNum] = indexUsed;
8     indexUsed++;
9     buffer[stackPointer[stackNum]]=new StackNode(lastIndex,value);
10 }

11 int pop(int stackNum) {
12    int value = buffer[stackPointer[stackNum]].value;
13    int lastIndex = stackPointer[stackNum];
14    stackPointer[stackNum] = buffer[stackPointer[stackNum]].previous;
15    buffer[lastIndex] = null;
16    indexUsed--;
17    return value;
18 }

19 int peek(int stack) { return buffer[stackPointer[stack]].value; }

20 boolean isEmpty(int stackNum) { return stackPointer[stackNum] == -1; }
21
22 class StackNode {
23    public int previous;
24    public int value;
25    public StackNode(int p, int v){
26       value = v;
27       previous = p;
28    }
29 }

我的问题是:在 pop() 方法中,将 buffer[lastIndex] 设置为 null(第 15 行),然后 indexUsed--(第 16 行),indexUsed 是否会成为空白空间的头部?让我们调用第一个堆栈的堆栈顶部节点:S1,第二个堆栈:S2,第三个堆栈:S3;如果出现以下情况会发生什么情况:在我想弹出 s1 之前,我先推送了 s1,然后是 s2 和 s3,

index:    10   11   12    13
-------------------------------
| ...   | s1 | s2 | s3 | null |
-------------------------------

indexUsed现在是13。现在,如果我想在第一个堆栈上执行 pop(),它将变为:

index:     10    11   12    13
----------------------------------
| ...   | null | s2 | s3 | null |
----------------------------------

并且indexUsed现在是 13-- ,即 12,所以如果我想根据 push() 方法将一个新节点推送到这个数组上会发生什么,在第 7 行,indexUsed 被分配给 stackPointer 并用作将索引推入数组,这不会覆盖第 12 个条目中的 s3(stack3 的顶部节点)吗?

added:

以及如何更改它以使其工作?乍一看,我会考虑实现一个 boolean[] 来跟踪存储阵列中每个条目的使用情况,但这会耗费时间和空间。

谢谢!

4

2 回答 2

1

据我所知,你是对的——它没有存储足够的信息来指示内存中的漏洞。

堆栈的一大优点是它可以分配在数组列表而不是链接列表的顶部,从而节省了前一个/下一个指针的内存——这种实现消除了这个优势——很难想象一个应用程序这实际上是一个很好的解决方案。

于 2012-05-14T15:31:10.927 回答
0

“为了克服这个缺陷,我们可以维护一个空闲列表,整个数组空间最初会被分配给空闲列表”

基本上你可以保留一个“第四个”堆栈,其中包含所有数组的空点。数组将在第 4 个堆栈已满时初始化。每次你推入堆栈 1/2/3 时,你都会从第 4 个堆栈中弹出一个。每次从 1/2/3 弹出时,您都会将其推回 4 号。这样,您始终知道空闲插槽在哪里。

于 2013-03-17T20:43:42.493 回答