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