結構之美學習二《棧和隊列》

來源:互聯網
上載者:User

棧和隊列其實是個抽象的資料類型。

對於他們的實現,既可以用數組,也可以用鏈表實現。

對於棧和隊列,看書的時候可能僅僅只是瞭解的一個概念。

因為要在程式中真正運用上去,個人覺得又是一個高度的思想境界。

我只是看了書,瞭解基本概念。

一。棧

定義

棧是僅限在表尾進行插入和刪除的操作線性表。
我們把允許插入和刪除資料的一段稱之為棧頂(top)。另一端稱之為棧底(bottom)
不含任何資料元素的棧稱為空白棧。LIFO後進先出表。

1.棧的數組結構實現,也就是棧順序儲存結構定義。

typedef struct {ElemType data[MAX_SIZE];int top;}SqStack;

棧的常見操作就是進棧push好出棧pop操作

Status push(SqStack * s, ElemType e){if(s->top == MAX_SIZE-1);//棧已經滿了return error;//入棧s->top++;s->data[s->top] = e;return ok;}Status pop(SqStack * s, ElemType *e){if(s->top ==-1)return error;*e = s->data[s->top];s->top--;return ok;}

對於棧的順序結構實現,是有很多的局限性的。

之後有又提出一個概念是 共用棧。

兩個棧共用空間。
棧的順序儲存結構就和線性表的順序儲存一樣,同樣存在對開闢的儲存空間無法很好的確定。
如果此時有兩個相同類類型的棧。他們各自用一個數組空間。此時極有可能其中一個棧想插入元素卻要溢出了。

而另外一個棧還留有很多儲存空間。此時可以稍微 改進下,讓兩個棧共用一個數組空間。
我們將數組的始端作為第一個棧的棧底,數組的末端最為另一個棧的棧底。兩個棧都是像中間延伸。

Status Push(SqStack * s, ElemType e, int stackNumber){ if(s->top1+1 == s->top2);    return error;if(stackNumber == 1){ s->data[++s->top1] = e;}else if(stackNumber == 2){s->data[--s->top2] = e;}return ok;}

2.棧的鏈式實現。

typedef struct StackNode{ElemType data;struct StackNode *next;}StackNode *LinkStackheadtypedef struct LinkStack{LinkStackhead top;//棧頂指標int count;//元素個數}

進棧和出棧操作

Status push(LinkStack * ls,ElemType e){LinkStackhead l = (LinkStack)malloc(sizeof(StackNode));//開闢一Block Storage空間l->data = e;//賦值l->next = ls->top;ls->top = l;ls->count++;return ok;}Status pop(LonkStack * ls ,ElemType *e){LinkStakchead h;if(ls->count == -1)return error;//*e = ls->top;h = ls->topls->top = p->next;free(h)ls->count--;return ok;}

棧的應用。

在現實中,先進後出的式的棧應用:比如檔案瀏覽工具就是一個典型。

其次比如我們程式的執行順序也是可以理解為一個棧的形式 。 A 方法中調用了B,B中調用了C。

那麼要執行完這端代碼,我們先按調用的順序將A,B,C依次壓入棧中,然後C  B  A依次pop出執行操作。

因此我們可以聯想到一個最常見的應用情境,就是遞迴。

遞迴中有個必定聽到過的的內容,就是Fibonacci數列。

斐波那契的遞迴函式。

int Fbi(int i){if(i<2)return i == 0?0:1return Fbi(i-1)+Fbi(i-2);}

即,我要取得該數列中的第i個數,必須Crowdsourced Security Testing道位於第 i-2和i-1這兩個數,

於是就產生一個遞迴的演算法。

在遞迴中要注意的演算法必須有跳出的條件。

從代碼可以看出,遞迴用到的是選擇結構。

顯然遞迴代碼很優雅,但是優雅是要有代價的,他會不斷調用和建立函數副本,耗費大量的時間和記憶體。
對於斐波那契數列求第i相的演算法有一種改進的做法,

因為這邊我們假設計算 i項的數,

那麼要先求 i-1和i-2項的數,

求i-1項的數則又需要求i-2和i-3項數;

到這邊,我們就看出來了,在求i項時,我們去計算了 i-2項,在求i-1項時,我們有去求了i-2項。

也就是說,其實在計算第i項數,不停的遞迴時,很多計算都是重複的。

那麼覺得可以考慮是否把每項的數在一次計算後就存起來,後面被調用到時在先去尋找該項是否已經計算,有的話就直接取出來,不存在那就計算咯

private  Map numsMap = new HashMap<Integer,Integer>();public  int Fbi(int i){int x,y;if(i<2)return i == 0?0:1;if(numsMap.get(Integer.valueOf(i-1)) !=null){x = (Integer) numsMap.get(Integer.valueOf(i-1));}else{x=Fbi(i-1);}if(numsMap.get(Integer.valueOf(i-2)) !=null){y = (Integer) numsMap.get(Integer.valueOf(i-2));}else{y=Fbi(i-2);}return x+y;}

抱歉我的java和C的混寫。因為C的話文法不是太熟,為了測試,熟練度java寫起來比較快。

這也算是空間換時間的一種做法。

     棧另外一個重要的的應用:四則運算運算式求值。
1.尾碼標記法,(逆波蘭):簡單說就是遇到數字就進棧,遇到符號就棧頂兩個數字出棧。
2.中綴運算式也就是我們平常書寫的四則運算運算式。
尾碼運算式雖簡單,但是怎麼把一個中綴運算式轉成一個尾碼呢?
同樣的,從左往右遍曆,若是數字就輸出,若是符號,判斷其於棧頂符號的優先順序,若是右括弧或是優先順序低於棧內元素的,

則棧頂元素依次出棧並輸出,將當前符號進棧。

二:隊列

定義

隊列是只允許在一端進行插入操作,在另一端進行刪除操作。FIFO線性表。

1.隊列順序儲存結構

相對於棧在插入和刪除上操作時間為O(1),在隊列插入元素只是在數組末端追加一個元素,時間也為O(1),

但是如果是出列一個元素,此時後面的元素都需要往前面移動一個位置,時間就是O(N)。

因此提出了隊頭隊尾指標。front指向隊頭元素,rear指向隊尾元素的下一個位置。

但此時還是會出現一個問題,就是不停的出隊入隊,front和rear一直在往後移動,前面的數組單元空著,但是指標已移動到數組末尾,

此時造成一個假溢出的現象。

於是又提出一個改進思路,迴圈隊列。

隊列頭尾相接的順序儲存結構稱之為迴圈隊列。
此時當在數組最後位置插入元素後,rear指標移動到數組的0的位置,既形成一個迴圈。
但是又會出現一個問題,在我繼續插入元素,rear指標往後移動,就會有rear等於front的時候。上面我們說這個情況的隊列是空隊列出現矛盾,既我們無法判斷當rear等於front時,隊列到底是滿呢,還是空的?
方法一:設定一個標誌位Tag,rear還沒有迴圈到數組0時,tag為0,迴圈到tag設定為1.
方法二:當隊列空時rear = front,,當隊列滿時,我們保留一個元素空間
計算隊列的長度通用公式:(rear -front +QueueSize)%QueueSize;
隊列滿的判斷 (rear -front +QueueSize)%QueueSize ==front;

迴圈隊列的順序結構定義

typedef struct{ElemType data[MAX_SIZE];int front;int rear;}SqQueue;

迴圈隊列的入隊出隊操作

Status EnQueue(SqQueue *Q,ElemType e){if((Q->rear -Q->front +MAXSZIE)%MAXSIZE ==Q->front)return error;Q->data[Q->rear] = e;Q->rear = (Q->rear+1)%MAXSZIE;return ok;}Status DeQueue(Sqqueue *Q,ElemType *e){if(Q->rear ==  Q->front)rerurn error;*e = Q- >data[Q->front];Q->front = (Q->front+1)%MAXSIZE;return ok;}

2.隊列的鏈式儲存實現

鏈隊列的結構
typedef int Elemtype ;typedef struct QNode{   ElemType data;   struct QNode  *next;}QNode,*QueuePtr;typedef struct{//隊列的鏈表結構   Queueptr front,rear;}LinkQueue;

鏈式隊列的插入和刪除

Status EnQueue(LinkQueue * Q , ElementType e){Queueptr p = (Queueptr) malloc(sizeof(QNode));if(!s)//分配失敗;exit(OVERFLOW);p->data = e ;P->next = NULL;Q->rear->next = p ;//在隊尾插入Q->rear = s ;//將p作為隊尾節點return ok ;}Status DeQueue(LinkQueue * Q , ElementType *e){Queueptr p ;if( Q-> front == Q-> rear) //空隊列return error;p = Q->front->next;//儲存要刪除的元素*e = p->data;Q->front->next = p->next;if(Q->rear == p)//若隊頭是隊尾,刪除後將隊尾指向隊頭Q->rear = Q->front;free(p);eturn ok;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.