雞啄米:C++程式設計入門系列之五(運算子和表示式)

qwer1030274531發表於2020-11-09

棧和佇列的定義和特點

  • 棧和佇列是兩種常用、重要的線性結構,從資料結構角度上來看也是線性表。
  • 棧和佇列是限定插入和刪除只能在表的“端點”進行操作的線性表。

:是限定僅為表尾進行插入或者刪除的線性表。因此,對棧來說,表尾端有其特殊含義,成為 棧頂(top),相應的,表頭端稱為 棧底(bottom)。不含元素的空表稱為空棧。棧是一種 後進先出的線性表;

與棧相反, 佇列(queue)是一種 先進先出(first in first out,縮寫為FIFO)的線性表,它只允許在表的一端進行插入,而在另一端刪除元素。在佇列中,允許插入的一端叫做 隊尾(rear),允許刪除的一端則稱為 隊頭(front)。

棧的表示和實現

棧的插入操作,叫做進棧,也稱壓棧、入棧。
棧的刪除操作,叫作出棧,也有的叫彈棧。
在這裡插入圖片描述

棧的順序儲存結構

我們定義一個top變數來指示棧頂元素在陣列中的位置,類似於小時候學過的油遊標卡尺,它的來回移動就意味著棧頂top可以變大變小。當棧存在一個元素是,top等於0,因此通常把空棧的判斷條件定為top等於-1.

棧的結構定義

typedef int SElemType;typedef struct{
	SElemTye data[MAXSIZE];
	int top;      /* 用於棧頂指標 */}SqStack;123456
棧的順序儲存結構——進棧操作

對於棧的插入,即進棧操作(如下圖所示):

在這裡插入圖片描述
程式碼如下:

/* 插入元素e為新的棧頂元素 */Status Push(SqStack *s, SElemType e){
	if(s->top == MAXSIZE -1)    // 棧滿
	{
		return ERROR;
	}
	s->top++;               /* 棧頂指標加一 */
	s->data[s->top] = e;    /* 將新插入元素賦值給棧頂空間 */}12345678910
棧的順序儲存結構——出棧操作

出棧操作pop

在這裡插入圖片描述
程式碼如下:

/* 若棧不空,則刪除s的棧頂元素,用e返回其值,並返回OK;否則返回ERROR*/Status Pop(SqStack *s,SElemType *e){
	if(s->top == -1)
		return ERROR;
	*e = s->data[s->top];   /* 將要刪除的棧頂元素賦值給e */
	s->top--;               /* 棧頂指標減一 */
	return OK;      }123456789
  • 兩者沒有涉及到任何迴圈語句,因此時間複雜度均為O(1)。 /henan/
棧的鏈式儲存結構

對於鏈棧來說,是不需要頭結點的。基本不存在滿棧的情況,除非已經沒有可以使用的空間,如果真的發生那麼此時計算機作業系統已經面臨當機崩潰的情況,而不是鏈棧溢位的問題。
對於空棧來說,連結串列原定義是頭指標指向空,那麼鏈棧的空其實就是top=NULL。

鏈棧的結構程式碼如下:

typedef struct StackNode{
	SElemType data;
	struct StackNode *next;}StackNoda,*LinkStackPtr;typedef struct LinkStack{
	LinkStackPtr top;
	int count;}LinkStack;1234567891011
棧的鏈式儲存結構——進棧操作

對於鏈棧的進棧push操作,假設元素值為e的新結點是s,top為棧頂指標。
程式碼如下:

Status Push(LinkStack *S, SElemType e){
	LinkStackPtr s =(LinkStackPtr)malloc(sizeof(StackNode));
	s->data=e;
	s->next=S->top;    /* 把當前的棧頂元素賦值給新結點的直接後繼 */
	S->top = s;       /*  將新的結點s賦值黑棧頂指標 */
	S->count++;
	return OK;}123456789
棧的鏈式儲存結構——出棧操作

對於鏈棧的pop操作,也是很簡單的三句操作。假設用變數 p 用來儲存要刪除的棧頂結點,將棧頂指標下移一位,最後釋放p即可。
程式碼表示如下: jiyuan/

/* 若棧不空,則刪除S的棧頂元素,用e返回其值,並返回OK;否則返回ERROR */Status Pop(LinkStack *S, SElemType *e){
	LinkStackPtr p;
	if(StackEmpty(*S))
		return ERROR;
	*e = S->top->data;
	p=S->top;                /* 將棧頂結點賦值給p */
	S->top = S->top->next;   /* 使得棧頂指標下移一位,指向後一結點 */
	free(p);
	S->count--;
	return OK;}12345678910111213

如果棧的使用過程中元素變化不可預料,有時很小,有時非常大,那麼最好是用鏈棧,反之,如果它的變化在可控範圍內,建議使用順序棧會更好一些 /question/


佇列的表示和實現

佇列是隻允許在一端進行插入操作,而在另一端進行刪除操作的線性表。

與棧不同的是,佇列元素的出列是在隊頭,即下標為0的位置,也就是意味著,佇列中的所有元素都得想前移動,以確保佇列的隊頭也就是小標為0的位置不為空,此時時間複雜度為O(n),如下圖所示:

(手繪多包涵)
在這裡插入圖片描述
為了避免當只有一個元素時,隊頭和隊尾重合使處理編的麻煩,所有引入兩個指標, front 指標指向隊頭元素, rear 指標指向隊尾元素的下一個位置,這樣當front等於rear時,此佇列不是還剩一個元素,而是空佇列。

假如這個佇列的總個數不超過5個,但是目前如果接著入隊的話,因陣列末尾元素已經佔用,再向後加,就會產生陣列越界的錯誤,可實際上,我們的佇列在下標為0和1的地方還是空閒的。我們把這種現象稱為“假溢位”。
因此引入迴圈佇列概念。

另外,當rear > front 時,此時佇列的長度為 rear - front 。但當 rear < front 時,佇列長度分為兩段,一段是QueueSize - front,另一段是0 + rear ,加在一起,佇列長度為 rear - front + QueueSize。因此通用的計算佇列長度公式為:
(rear - front + QueueSize)% QueueSize

佇列的鏈式儲存結構及實現

佇列的鏈式儲存結構,其實就是線性表的單連結串列,只不過它只能尾進頭出而以,我們通常簡稱它為鏈佇列。
為了方便操作,我們將頭指標指向鏈佇列的頭結點,而隊尾指標指向終端節點。

在這裡插入圖片描述
空佇列,front 和 rear 都指向頭結點。

鏈佇列的結構為: anhui/

typedef int QElemType; /* QElemType 型別根據實際情況而定 */typedef struct QNode  /* 姐弟啊結構 */{
	QElemType data;
	struct QNode *next;}QNode,*Queueptr;typedef struct              /* 佇列的連結串列結構 */{
	QueuePtr front,rear;   /* 隊頭、隊尾指標 */}LinkQueue;123456789101112
佇列的鏈式儲存結構——入隊操作

入隊操作時,其實就是在連結串列尾部插入結點。
其程式碼如下: zhumadian/

/* 插入元素e為Q的新的隊尾元素 */Status EnQueue(LinkQueue *Q, QElemType e){
	QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
	if(!s)     exit(OVERFLOW);   /* 儲存記憶體分配失敗 */
	s->data = e;
	s->next = NULL;
	Q->rear->next=s;           /* 把擁有元素 e 新結點 s 賦值給原佇列結點的後繼 */
	Q->rear=s;                 /* 把當前的 s 設定為隊尾結點, rear 指向 s */
	return OK;}1234567891011
佇列的鏈式儲存結構——出隊操作

出隊操作時,就是頭結點的後繼結點出隊,將頭結點的後繼改為它後面的結點,若連結串列除頭結點外只剩下一個元素時,則需要將 rear 指向頭結點。

程式碼如下: heilongjiang/

/* 若佇列不空,刪除Q的隊頭元素,用e返回其值,並返回OK */Status DeQueue(LinkQueue *Q, QElemType *e){
	QueuePtr p;
	if(Q->front == Q->rear)
		return ERROR;
	P = Q->front->next; /* 將欲刪除的隊頭結點暫存給p */
	*e = p->data;       /* 將欲刪除的隊頭結點的值賦值給e */
	Q->front->next = p->next;  /* 將原隊頭結點後繼p->next 賦值給頭結點後繼 */
	if(Q->rear = p)          /* 若隊頭是隊尾,則刪除後將rear指向頭結點 */
		Q->rear = Q->front;
	free(p);
	return OK;}1234567891011121314

總的來說,在確定佇列長度最大值的情況下,建議用迴圈佇列,如果你無法預估佇列的長度時,則用鏈佇列。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30239065/viewspace-2732922/,如需轉載,請註明出處,否則將追究法律責任。

相關文章