如何使用单个数组实现三个堆栈

时间:2010-06-17 08:37:15

标签: language-agnostic data-structures

我在面试网站上遇到过这个问题。该问题要求在单个阵列中有效地实现三个堆栈,使得在整个阵列空间中没有剩余空间之前没有堆栈溢出。

为了在数组中实现2个堆栈,很明显:第一个堆栈从LEFT变为RIGHT,第二个堆栈从RIGHT增长到LEFT;当stackTopIndex交叉时,它会发出溢出信号。

提前感谢您的深刻回答。

17 个答案:

答案 0 :(得分:15)

您可以使用linked list实现三个堆栈:

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

链接列表可以在数组中实现。

这是怎样的(空间)效率? 通过为每个列表元素(值+指针)使用数组的两个单元格来构建链接列表没有问题。根据规范,您甚至可以将指针和值放入一个数组元素中(例如,数组很长,值和指针只是int)。
将此与kgiannakakis ...的解决方案进行比较,其中您损失高达50%(仅在最坏的情况下)。但我认为我的解决方案有点清洁(也许更多学术,这对面试问题来说应该没有劣势^^)。

答案 1 :(得分:8)

Knuth, The Art of Computer Programming, Volume 1,第2.2.2节。标题为“顺序分配”。讨论在单个数组中分配多个队列/堆栈,算法处理溢出等等。

答案 2 :(得分:3)

我们可以使用表示第i个阵列单元所属堆栈的长位数组。 我们可以通过模3(00 - 空,01 - A,10 - B,11 - C)取值。对于N大小的阵列,需要N / 2比特或N / 4字节的附加存储器。

例如,对于1024个长int元素(4096字节),它只需要256个字节或6%。

这个位数组映射可以在开头或结尾放在同一个数组中,只需将给定数组的大小缩小6%即可!

答案 3 :(得分:2)

第一个堆栈从左到右增长。

第二个堆栈从右向左增长。

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

* * * * * * * * * * *
      5 3 1 2 4

允许第一和第二堆栈在阵列的一半大小处最大化。第三个堆栈可以增长以最大限度地填充整个阵列。

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

答案 4 :(得分:2)

这是一个有趣的难题,我没有一个真正的答案,但在框外思考......

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

我知道这有点作弊,并没有真正回答这个问题,但它确实适用于一个非常具体的情况,并且没有浪费堆栈中的条目。我很感兴趣地看到是否有人能够提出适用于更通用元素的正确答案。

答案 5 :(得分: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/3边界然后你有30%的空间浪费。

答案 6 :(得分:1)

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

如果您执行两个堆栈解决方案,将第三个堆栈放在中间的某个位置,并跟踪其底部和顶部,然后大多数操作将继续高效,但是会损失昂贵的Move操作(第三个堆栈)每当碰撞发生时,朝向自由空间的任何地方,移动到自由空间的中间点。

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

答案 7 :(得分:1)

假设您只有整数索引。如果使用FILO(先进先出)处理它并且不引用个体,并且仅使用数组作为数据。使用它的6个空格作为堆栈引用应该有帮助:

  

[ head-1,last-1,head-2,last-2,head-3,last-3, 数据,数据,...,数据]

你可以简单地使用4个空格,因为head-1 = 0而last-3 =数组长度。如果使用FIFO(先进先出),则需要重新编制索引。

nb:我正努力提高英语水平。

答案 8 :(得分:1)

  1. 使用键到开头和结尾位置创建一个HashMap,例如< " B1" ,0>,<&#;;" E1" ,n / 3>

  2. 为每个Push(值)添加一个条件来检查Bx的位置是否在Ex的前面或者是否有其他的" By"之间。 - 让我们称之为条件(2)

  3. 考虑到上述情况, 如果上面的(2)是真的//如果B1和E1是有序的     {if(S1.Push()),然后是E1 ++;       否则//溢出的情况,       {在E2或E3结束时开始推动(以空格为准)并将E1更新为E2--或E3--; }     }

    如果上面的(2)是假的       {if(S1.Push()),然后E1 - ;         否则//溢出的情况,           {在E2或E3结束时开始推动(以空格为准)并将E1更新为E2--或E3--; }       }

答案 9 :(得分:1)

一个相当愚蠢但有效的解决方案可能是:

  • 将第一个堆叠元素存储在i*3个位置:0,3,6,...
  • 将第二个堆叠元素存储在i*3+1个位置:1,4,7 ......
  • 位于i*3+2位置的第三个堆叠元素。

此解决方案的问题在于,使用的内存总是三倍于最深堆栈的大小,即使阵列中有可用位置,也可能溢出。

答案 10 :(得分:0)

也许您可以在单个阵列中实现N个堆栈或队列。我对使用单个数组的定义是,我们使用单个数组将所有堆栈和队列的所有数据存储在单个数组中,无论如何,我们可以使用其他N数组来跟踪特定堆栈或队列的所有元素的索引。

解决方案: 在插入堆栈或队列中的任何一个期间将数据顺序存储到阵列中。并将其各自的索引存储到该特定堆栈或队列的索引保持数组中。

例如:您有3个堆栈(s1,s2,s3),并且您想使用单个数组(dataArray [])来实现。因此,我们将分别为s1,s2和s3创建其他3个数组(a1 [],a2 [],a3 []),这些数组将通过保存各自的索引来跟踪dataArray []中的所有元素。

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;

以此类推...

现在我们将通过分别对堆栈和队列使用a1,a2和a3在dataArray []中执行操作。

从s1弹出元素     返回a1 [0]     将所有元素向左移

对其他操作也执行类似的方法,您可以在单个数组中实现任意数量的堆栈和队列。

答案 11 :(得分:0)

可能这可以帮助你...我自己写的 :)



// by ashakiran bhatter
// compile: g++ -std=c++11 test.cpp
// run    : ./a.out
// sample output as below
// adding:  1 2 3 4 5 6 7 8 9
// array contents:  9 8 7 6 5 4 3 2 1
// popping now...
// array contents:  8 7 6 5 4 3 2 1


#include <iostream>
#include <cstdint>

#define MAX_LEN 9
#define LOWER   0
#define UPPER   1
#define FULL   -1
#define NOT_SET -1

class CStack
{
private:
     int8_t array[MAX_LEN];
     int8_t stack1_range[2];
     int8_t stack2_range[2];
     int8_t stack3_range[2];
     int8_t stack1_size;
     int8_t stack2_size;
     int8_t stack3_size;
     int8_t stack1_cursize;
     int8_t stack2_cursize;
     int8_t stack3_cursize;
     int8_t stack1_curpos;
     int8_t stack2_curpos;
     int8_t stack3_curpos;
public:
     CStack();
    ~CStack();

     void push(int8_t data);
     void pop();
     void print();
};

CStack::CStack()
{
     stack1_range[LOWER] = 0;
     stack1_range[UPPER] = MAX_LEN/3 - 1;
     stack2_range[LOWER] = MAX_LEN/3;
     stack2_range[UPPER] = (2 * (MAX_LEN/3)) - 1;
     stack3_range[LOWER] = 2 * (MAX_LEN/3);
     stack3_range[UPPER] = MAX_LEN - 1;

     stack1_size = stack1_range[UPPER] - stack1_range[LOWER];
     stack2_size = stack2_range[UPPER] - stack2_range[LOWER];
     stack3_size = stack3_range[UPPER] - stack3_range[LOWER];

     stack1_cursize = stack1_size;
     stack2_cursize = stack2_size;
     stack3_cursize = stack3_size;

     stack1_curpos = stack1_cursize;
     stack2_curpos = stack2_cursize;
     stack3_curpos = stack3_cursize;
}

CStack::~CStack()
{
}

void CStack::push(int8_t data)
{
     if(stack3_cursize != FULL) 
     {
          array[stack3_range[LOWER] + stack3_curpos--] = data;
          stack3_cursize--;
     } else if(stack2_cursize != FULL) {
          array[stack2_range[LOWER] + stack2_curpos--] = data;
          stack2_cursize--;
     } else if(stack1_cursize != FULL) {   
          array[stack1_range[LOWER] + stack1_curpos--] = data;
          stack1_cursize--;
     } else {
          std::cout<<"\tstack is full...!"<<std::endl;
     }
}

void CStack::pop()
{
     std::cout<<"popping now..."<<std::endl;
     if(stack1_cursize < stack1_size)
     {
          array[stack1_range[LOWER] + ++stack1_curpos] = 0; 
          stack1_cursize++;
     } else if(stack2_cursize < stack2_size) {
          array[stack2_range[LOWER] + ++stack2_curpos] = 0;
          stack2_cursize++;
     } else if(stack3_cursize < stack3_size) {
          array[stack3_range[LOWER] + ++stack3_curpos] = 0;
          stack3_cursize++;
     } else {
          std::cout<<"\tstack is empty...!"<<std::endl;
     }
}

void CStack::print()
{
     std::cout<<"array contents: ";
     for(int8_t i = stack1_range[LOWER] + stack1_curpos + 1; i <=  stack1_range[UPPER]; i++)
          std::cout<<" "<<static_cast<int>(array[i]);
     for(int8_t i = stack2_range[LOWER] + stack2_curpos + 1; i <=  stack2_range[UPPER]; i++)
          std::cout<<" "<<static_cast<int>(array[i]);
     for(int8_t i = stack3_range[LOWER] + stack3_curpos + 1; i <=  stack3_range[UPPER]; i++)
          std::cout<<" "<<static_cast<int>(array[i]);
     std::cout<<"\n";    
}

int main()
{
     CStack stack;

     std::cout<<"adding: ";
     for(uint8_t i = 1; i < 10; i++) 
     {
          std::cout<<" "<<static_cast<int>(i);
          stack.push(i);
     }
     std::cout<<"\n";

     stack.print();

     stack.pop();

     stack.print();


     return 0;
}
&#13;
&#13;
&#13;

答案 12 :(得分:0)

PYTHON的另一个解决方案,请告诉我这是否与您的想法一致。

    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

答案 13 :(得分:0)

此代码在单个数组中实现3个堆栈。它处理空白空间并填充数据之间的空白区域

  

#include&lt; stdio.h&gt;
  
  struct stacknode {
  int值;
  int prev;
  };
  
  struct stacknode stacklist [50];
  int top [3] = {-1,-1,-1};
  int freelist [50];
  int stackindex = 0;
  int freeindex = -1;
  
  void push(int stackno,int value){
  int index;
  if(freeindex&gt; = 0){
  index = freelist [freeindex];
  freeindex--;
  其他{
  index = stackindex;
  stackindex ++;
  }
  stacklist [index] .value = value;
  if(top [stackno-1]!= -1){
  stacklist [index] .prev = top [stackno-1];
  其他{
  stacklist [index] .prev = -1;
  }
  top [stackno-1] = index;
  printf(“%d在堆栈%d中被推入%d \ n”,value,stackno,index);
  }
  
  int pop(int stackno){
  int index,value;
  if(top [stackno-1] == -1){
  printf(“堆栈中没有元素%d \ n”,value,stackno);
  返回-1;
  }
  index = top [stackno-1];
  freeindex ++;
  freelist [freeindex] = index;
  value = stacklist [index] .value;
  top [stackno-1] = stacklist [index] .prev;
  printf(“%d从堆栈%d弹出,在%d \ n”,value,stackno,index);

  返回值;
  }
  
  int main(){
  推(1,1);
  推(1,2);
  推(3,3);
  推(2,4);
  弹出(3);
  弹出(3);
  推(3,3);
  推(2,3);
  }

答案 14 :(得分:0)

在这种方法中,只要数组中有任何空闲空间,任何堆栈都可以增长。 我们按顺序为堆栈分配空间,并将新块链接到前一个块。这意味着堆栈中的任何新元素都会保留指向该特定堆栈的前一个顶部元素的指针。

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;
    }
}

答案 15 :(得分:0)

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

aacba
54321

计算适当的map值,同时将元素压入堆栈(使用数组的移位内容)

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

和堆栈3,1,1的长度 一旦你想要做c.pop(),你必须通过在单元格中行走来找到c.top()在原始数组中的实际位置来重新组织你的元素(即除以3而mod为3 isn' t 2)然后将阵列中的所有内容移回以覆盖该孔。在遍历单元格地图时,您必须存储您已经通过的所有位置(mapX),并且在传递指向堆叠“c”的位置之后,您将不得不再将3除以另一个时间并将其相乘通过3 ^(数量位置传递-1)并添加mapX以获得细胞映射的新值 固定的开销并取决于堆栈元素的大小(bits_per_value):
(log(3 n)/ log(2))/(n log(bits_per_value)/ log(2))= log(3 n)/( n log(bits_per_value))= log(3)/ log(bits_per_value)
因此,对于bits_per_value = 32,它将占31.7%的空间开销,并且随着bits_per_value的增长,它将衰减(即,对于64位,它将是26.4%)。

答案 16 :(得分:0)

第一个堆栈增长到3n, 第二个堆栈增长到3n + 1, 第三个增长为3n + 2

表示n = {0 ... N}