Language agnostic 如何使用单个阵列实现三个堆栈

Language agnostic 如何使用单个阵列实现三个堆栈,language-agnostic,data-structures,Language Agnostic,Data Structures,我在一个采访网站上遇到了这个问题。这个问题要求在单个阵列中高效地实现三个堆栈,这样,在整个阵列空间中没有剩余空间之前,堆栈不会溢出 对于在一个数组中实现两个堆栈,这是非常明显的:第一个堆栈从左向右增长,第二个堆栈从右向左增长;当stackTopIndex交叉时,表示溢出 提前感谢您富有洞察力的回答 第一个堆栈从左向右增长 第二个堆栈从右向左增长 第三层从中间开始。为了简单起见,假设奇数大小的数组。然后第三个堆栈的增长如下: * * * * * * * * * * * 5 3 1 2

我在一个采访网站上遇到了这个问题。这个问题要求在单个阵列中高效地实现三个堆栈,这样,在整个阵列空间中没有剩余空间之前,堆栈不会溢出

对于在一个数组中实现两个堆栈,这是非常明显的:第一个堆栈从左向右增长,第二个堆栈从右向左增长;当stackTopIndex交叉时,表示溢出


提前感谢您富有洞察力的回答

第一个堆栈从左向右增长

第二个堆栈从右向左增长

第三层从中间开始。为了简单起见,假设奇数大小的数组。然后第三个堆栈的增长如下:

* * * * * * * * * * *
      5 3 1 2 4
第一个和第二个堆栈允许以阵列的一半大小最大增长。第三个堆栈最多可以填充整个阵列

最坏的情况是,前两个阵列中的一个阵列以阵列的50%增长。那么阵列就浪费了50%。为了优化效率,必须选择第三个阵列,使其比其他两个阵列增长更快。

第一个堆栈以3n的速度增长, 第二个堆栈在3n+1处增长, 第三个在3n+2处生长


对于n={0…n}

,您可以使用以下命令实现三个堆栈:

  • 您需要一个指向下一个自由元素的指针。另外三个指针返回每个堆栈的最后一个元素(如果堆栈为空,则返回null)
  • 当堆栈添加另一个元素时,它必须使用第一个自由元素并将自由指针设置为下一个自由元素(否则将引发溢出错误)。它自己的指针必须指向新元素,从那里返回到堆栈中的下一个元素
  • 当一个堆栈移除一个元素时,它会将其返回到自由元素列表中。堆栈自身的指针将重定向到堆栈中的下一个元素
链表可以在数组中实现

这(空间)效率如何?
通过为每个列表元素(值+指针)使用两个数组单元格来构建链表是没有问题的。根据规格,您甚至可以将指针和值放入一个数组元素中(例如,数组很长,值和指针仅为int)。

将此与…的解决方案进行比较。。。损失高达50%(仅在最坏的情况下)。但我认为我的解决方案更简洁(也许更学术,这对面试问题来说应该没有坏处。^^^)。

这是一个有趣的难题,我没有真正的答案,只是稍微跳出框框思考

它可能取决于堆栈中的每个元素由什么组成。如果是三堆真/假标志,那么可以使用整数元素的前三位。即,位0是第一个堆栈的值,位1是第二个堆栈的值,位2是第三个堆栈的值。然后,每个堆栈可以独立增长,直到该堆栈的整个阵列都已满。这甚至更好,因为即使第一个堆栈已满,其他堆栈也可以继续增长


我知道这有点作弊,并没有真正回答问题,但它确实适用于非常具体的情况,堆栈中没有任何条目被浪费。我正在饶有兴趣地观察,看是否有人能想出一个适用于更通用元素的正确答案。

假设所有数组位置都应该用于存储值-我想这取决于您对效率的定义

如果执行两个堆栈解决方案,将第三个堆栈放置在中间,并跟踪它的底部和顶部,那么大多数操作将继续有效,在代价很高的移动操作(第三个堆栈向着自由空间的任何地方移动,移动到自由空间的半个点)的惩罚时,每当发生碰撞时。


它肯定会很快编码和理解。我们的效率目标是什么?

将阵列拆分为任意3个部分(无论您是按顺序拆分还是交错拆分)。如果一个堆栈的增长大于数组的1/3,则开始填充其余两个堆栈的末端

aaa bbb ccc 1 2 3 145 2 3 145 2 6 3 145 2 6 3 7 145 286 3 7 145 286 397 宽带路由器 1 2 3 145 2 3 145 2 6 3 145 2 6 3 7 145 286 3 7 145 286 397 更糟糕的情况是,当两个堆栈增长到1/3边界时,您有30%的空间浪费。

另一种方法(作为链表的补充)是使用堆栈映射。在这种情况下,您必须使用额外的log(3^n)/log(2)位来构建n长度数组中的数据分布图。映射的每个3值部分都表示哪个堆栈拥有下一个元素。 例如
a.push(1);b、 推(2);c、 推(3);a、 推(4);a、 推(5)将为您提供图像

aacba 54321 以及堆栈3,1,1的长度
一旦您想要执行
c.pop()
操作,您就必须通过在单元格映射中漫游来找到原始数组中
c.top()
的实际位置来重新组织元素(即除以3,而mod by 3不是2),然后将数组中的所有内容移回以覆盖该孔。在遍历单元格映射时,您必须存储所有经过的位置(
mapX
),在经过指向堆栈“c”的位置后,您必须再次除以3,再乘以3^(经过的位置数量-1),然后添加
mapX
,以获得单元格映射的新值。
固定的开销,并取决于堆栈元素的大小(
位/u值
):
(log(3n)/log(2))/(nlog(比特/比特值)/log(2))=log(3n)/(nlog(比特/比特值))=log(3)/log(比特/比特值)

因此,对于
位/u值=32
而言,空间开销将为31.7%,随着
位/u值的增加,空间开销将衰减(即,对于64位,空间开销将为26.4%)。

参见第2.2.2节。标题为“顺序分配”。讨论在单个数组中分配多个队列/堆栈,以及处理溢出等的算法。

我们可以使用长位数组表示第i个数组单元所属的堆栈。 我们可以取模3的值
map0 = any
map1 = map0*3 + 0
map2 = map1*3 + 1
map3 = map2*3 + 2
map4 = map3*3 + 0
map5 = map4*3 + 0 = any*3^5 + 45
int stackSize = 300;
int indexUsed = 0;
int[] stackPointer = {-1,-1,-1};
StackNode[] buffer = new StackNode[stackSize * 3];

void push(int stackNum, int value) {
    int lastIndex = stackPointer[stackNum];
    stackPointer[stackNum] = indexUsed;
    indexUsed++;
    buffer[stackPointer[stackNum]]=new StackNode(lastIndex,value);
}

int pop(int stackNum) {
    int value = buffer[stackPointer[stackNum]].value;
    int lastIndex = stackPointer[stackNum];
    stackPointer[stackNum] = buffer[stackPointer[stackNum]].previous;
    buffer[lastIndex] = null;
    indexUsed--;
    return value;
}

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

boolean isEmpty(int stackNum) { return stackPointer[stackNum] == -1; }

class StackNode {
    public int previous;
    public int value;

    public StackNode(int p, int v){
        value = v;
        previous = p;
    }
}
    class Stack(object):

    def __init__(self):
        self.stack = list()

        self.first_length = 0
        self.second_length = 0
        self.third_length = 0

        self.first_pointer = 0
        self.second_pointer = 1

    def push(self, stack_num, item):

        if stack_num == 1:
            self.first_pointer += 1
            self.second_pointer += 1
            self.first_length += 1
            self.stack.insert(0, item)

        elif stack_num == 2:
            self.second_length += 1
            self.second_pointer += 1
            self.stack.insert(self.first_pointer, item)
        elif stack_num == 3:
            self.third_length += 1
            self.stack.insert(self.second_pointer - 1, item)
        else:
            raise Exception('Push failed, stack number %d is not allowd' % stack_num)

    def pop(self, stack_num):
        if stack_num == 1:
            if self.first_length == 0:
                raise Exception('No more element in first stack')
            self.first_pointer -= 1
            self.first_length -= 1
            self.second_pointer -= 1
            return self.stack.pop(0)
        elif stack_num == 2:
            if self.second_length == 0:
                raise Exception('No more element in second stack')
            self.second_length -= 1
            self.second_pointer -= 1
            return self.stack.pop(self.first_pointer)
        elif stack_num == 3:
            if self.third_length == 0:
                raise Exception('No more element in third stack')
            self.third_length -= 1
            return self.stack.pop(self.second_pointer - 1)

    def peek(self, stack_num):
        if stack_num == 1:
            return self.stack[0]
        elif stack_num == 2:
            return self.stack[self.first_pointer]
        elif stack_num == 3:
            return self.stack[self.second_pointer]
        else:
            raise Exception('Peek failed, stack number %d is not allowd' % stack_num)

    def size(self):
        return len(self.items)

s = Stack()

# push item into stack 1
s.push(1, '1st_stack_1')
s.push(1, '2nd_stack_1')
s.push(1, '3rd_stack_1')
#
## push item into stack 2
s.push(2, 'first_stack_2')
s.push(2, 'second_stack_2')
s.push(2, 'third_stack_2')
#
## push item into stack 3
s.push(3, 'FIRST_stack_3')
s.push(3, 'SECOND_stack_3')
s.push(3, 'THIRD_stack_3')
#
print 'Before pop out: '
for i, elm in enumerate(s.stack):
    print '\t\t%d)' % i, elm

#
s.pop(1)
s.pop(1)
#s.pop(1)
s.pop(2)
s.pop(2)
#s.pop(2)
#s.pop(3)
s.pop(3)
s.pop(3)
#s.pop(3)
#
print 'After pop out: '
#
for i, elm in enumerate(s.stack):
    print '\t\t%d)' % i, elm
insert(s1, 10) at dataArray[0] a1[0] = 0;
insert(s2, 20) at dataArray[1] a2[0] = 1;
insert(s3, 30) at dataArray[2] a3[0] = 2;
insert(s1, 40) at dataArray[3] a1[1] = 3;
insert(s3, 50) at dataArray[4] a3[1] = 4;
insert(s3, 60) at dataArray[5] a3[2] = 5;
insert(s2, 30) at dataArray[6] a2[1] = 6;