簡體   English   中英

如何使用單個數組實現三個堆棧

[英]How to implement three stacks using a single array

我在一個采訪網站上遇到了這個問題。 該問題要求在單個陣列中有效地實現三個堆棧,使得在整個陣列空間中沒有剩余空間之前沒有堆棧溢出。

為了在數組中實現2個堆棧,非常明顯:第一個堆棧從LEFT變為RIGHT,第二個堆棧從RIGHT增長到LEFT; 當stackTopIndex交叉時,它會發出溢出信號。

提前感謝您的深刻回答。

您可以使用鏈接列表實現三個堆棧:

  • 你需要一個指向下一個自由元素的指針。 另外三個指針返回每個堆棧的最后一個元素(如果堆棧為空,則返回null)。
  • 當堆棧添加另一個元素時,它必須使用第一個free元素並將空閑指針設置為下一個free元素(否則將引發溢出錯誤)。 它自己的指針必須指向新元素,從那里返回到堆棧中的下一個元素。
  • 當堆棧中的元素被刪除時,它會將其移回自由元素列表中。 堆棧的自己的指針將被重定向到堆棧中的下一個元素。

鏈表可以在數組中實現。

怎么(空間)效率這個?
通過為每個列表元素(值+指針)使用數組的兩個單元格來構建鏈接列表沒有問題。 根據規范,你甚至可以將指針和值放到一個數組元素中(例如,數組很長,值和指針只是int)。
將此與kgiannakakis的解決方案進行比較 ......在此情況下,您將損失高達50%(僅在最壞的情況下)。 但我認為我的解決方案有點清潔(也許更多的學術 ,對面試問題來說應該沒有劣勢^^)。

參見Knuth,計算機程序設計的藝術,第1卷 ,第2.2.2節。 標題為“順序分配”。 討論在單個數組中分配多個隊列/堆棧,算法處理溢出等。

我們可以使用表示第i個陣列單元所屬的堆棧的長位數組。 我們可以通過模3(00 - 空,01 - A,10 - B,11 - C)取值。 對於N大小的陣列,需要N / 2比特或N / 4字節的附加存儲器。

例如,對於1024個長int元素(4096字節),它只需要256個字節或6%。

這個位數組映射可以在開頭或結尾放在同一個數組中,只需將給定數組的大小縮小6%即可!

第一個堆棧從左到右增長。

第二堆棧從右到左增長。

第三堆棧從中間開始。 為簡單起見,假設奇數大小的數組。 然后第三個堆棧增長如下:

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

允許第一和第二堆疊在陣列的一半大小處最大化。 第三個堆棧可以增長以最大程度地填充整個陣列。

最糟糕的情況是前兩個陣列中的一個以陣列的50%增長。 然后有50%的陣列浪費。 為了優化效率,必須選擇第三個陣列,使其比其他兩個陣列更快地增長。

這是一個有趣的難題,我沒有一個真正的答案,但在框外思考...

它可能取決於堆棧中每個元素的組成。 如果它是三個真/假標志棧,那么你可以使用整數元素的前三位。 IE瀏覽器。 bit 0是第一個堆棧的值,bit 1是第二個堆棧的值,bit 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%的空間浪費。

一個相當愚蠢但有效的解決方案可能是:

  • 將第一個堆棧元素存儲在i*3位置:0,3,6,...
  • 將第二個堆棧元素存儲在i*3+1位置:1,4,7 ......
  • 並且在i*3+2位置處的第三堆疊元件。

此解決方案的問題在於,使用的內存總是三倍於最深堆棧的大小,即使陣列中有可用位置,也可能溢出。

假設所有數組位置都應該用於存儲值 - 我想這取決於你對效率的定義。

如果你做了兩個堆棧解決方案,將第三個堆棧放在中間的某個位置,並跟蹤它的底部和頂部,那么大多數操作將繼續高效,而且會損失昂貴的Move操作(第三個堆棧朝向任何地方免費)每當碰撞發生時,空間仍然存在,移動到自由空間的中間點。

編碼和理解肯定會很快。 我們的效率目標是什么?

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

假設您只有整數索引。 如果使用FILO(先進先出)處理它並且不引用個體,並且僅使用數組作為數據。 使用它的6個空格作為堆棧引用應該有幫助:

[ head-1,last-1,head-2,last-2,head-3,last-3, data,data,...,data]

你可以簡單地使用4個空格,因為head-1 = 0而last-3 =數組長度。 如果使用FIFO(先進先出),則需要重新編制索引。

我正在努力提高自己的英語水平。

此代碼在單個數組中實現3個堆棧。 它處理空白空間並填充數據之間的空白空間。

#include <stdio.h>

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> = 0){
index = freelist [freeindex];
freeindex--;
} else {
index = stackindex;
stackindex ++;
}
stacklist [index] .value = value;
if(top [stackno-1]!= -1){
stacklist [index] .prev = top [stackno-1];
} else {
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);
}

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

可能這可以幫到你...我自己寫的:)

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

第一個堆棧增長為3n,第二個堆棧增長為3n + 1,第三個堆棧增長為3n + 2

對於n = {0 ... N}

另一種方法(作為鏈表的附加)是使用堆棧的映射。 在這種情況下,您將不得不使用額外的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()找到原始數組中c.top()實際位置來重新組織你的元素(即除以3而mod為3 isn' t 2)然后將陣列中的所有內容移回以覆蓋該孔。 在遍歷單元格映射時,您必須存儲所有已經傳遞的位置( mapX ),並且在傳遞指向堆棧“c”的位置之后,您將mapX除以3並將其乘以3 ^(數量位置傳遞-1)並添加mapX以獲取cells-map的新值。
固定的開銷,取決於堆棧元素的大小( 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%)。

在這種方法中,只要陣列中有任何空閑空間,任何堆棧都可以增長。 我們按順序為堆棧分配空間,並將新塊鏈接到前一個塊。 這意味着堆棧中的任何新元素都會保留指向該特定堆棧的前一個頂部元素的指針。

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

也許你可以在單個數組中實現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]將所有元素移到左邊

對其他操作也采用類似的方法,您可以在單個數組中實現任意數量的堆棧和隊列。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM