溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

數(shù)據(jù)結構之棧和隊列(C語言版)

發(fā)布時間:2020-07-19 11:23:26 來源:網(wǎng)絡 閱讀:1212 作者:捕風的xiao_k 欄目:編程語言




    數(shù)據(jù)結構學習繼續(xù)向前推進,之前對線性表進行了學習,現(xiàn)在我們進入棧和隊列的學習。同樣我們先學習一些基本概念以及堆棧的ADT.

棧和隊列是兩種中重要的線性結構。從數(shù)據(jù)結構角度看,棧和隊列也是線性表,只不過是受限的線性表。因此可以稱為限定性數(shù)據(jù)結構。但從數(shù)據(jù)類型來看,他們是和線性表大不相同的兩類重要的抽象數(shù)據(jù)類型。

棧:(stack)是限定僅在表尾進行相應插入和刪除操作的線性表。因此,對棧來說,表尾有其特殊含義,稱為棧頂,表頭稱為棧底,不含元素的空表稱為空棧。棧一個重要特性就是后進先出.OK,我們來看一下棧的結構:


數(shù)據(jù)結構之棧和隊列(C語言版)


ADT Stack

{

數(shù)據(jù)對象:D = {ai| ai屬于ElemSet,i = 1,2,……,n, n >= 0 }

約定an端為棧頂,a1端為棧底。

基本操作:

                Status Init_SeqStack(SeqStack *s)

                    初始化棧,構造一個空棧。

                Status Destory(SeqStack *s)

                    摧毀棧

                Status Clear(SeqStack *s)

                    清空棧                 

                int IsEmpty(SeqStack *s)

                判斷是否為空棧,是空棧返回1,不是空棧返回0

               int IsFull(SeqStack *s )

                判斷棧是否滿,棧滿返回1,沒有滿返回0

                int  GetLength(SeqStack s)

                返回棧中元素的個數(shù)

                Status PushStack(SeqStack *s,ElemType x)

                插入元素e為新的棧頂元素

                Status GetTop(SeqStack *s,ElemType *value)

                獲取棧頂元素,用value帶回

                Status  PopStack(SeqStack *s)

                刪除棧頂元素              

                Status Show_Stack(SeqStack s)

                打印輸出棧中元素

}




    以上就是棧的抽象數(shù)據(jù)類型,好了我們接下來用C語言實現(xiàn)棧結構,既然棧受限的線性表,線性表有鏈式存儲結構和順序存儲結構,那么棧也同樣有兩種,順序棧和鏈棧。依然沿襲過去先弄清結構再通過代碼解釋。開篇已經(jīng)講了棧是受限的線性表,那么我們就要類比著看棧的操作:順序棧,就類比順序表。鏈棧就類比單鏈表。ok,我們先來看順序棧:



順序棧


    順序棧,即就是棧的順序存儲結構,是利用一組連續(xù)的存儲單元一次依次存放自棧底到棧頂?shù)臄?shù)據(jù)元素,同時附設top指示棧頂元素在順序表中的位置,通常的習慣做法以top=0表示空棧,鑒于以C語言作為描述語言時,如此設定會帶來很大不便;另一方面,由于棧在使用過程中所需的最大空間大小無法估計。因此一般來說,先為初始化空棧時不應設定棧的最大空間容量。一個較合理的做法是:先為棧分配一個基本容量,然后在應用過程中,當棧的空間不夠時再逐段擴大。為此,可以設定兩個常 STACK_INIT_SIZE(存儲空間初始分配量)和STACK_INC_SIZE(存儲空間分配增量)


 ok我們先定義順序棧的結構:


#define ElemType char

#define Status    int 

#define FALSE     0

#define TRUE      1

#define STACK_INIT_SIZE  8               存儲空間初始分配量

#define STACK_INC_SIZE   20              存儲空間分配增量

typedef struct  SeqStack

{

ElemType *bace;

int capacity;

int top;

}SeqStack;



//判斷是否為空棧,棧結構中的top也就記錄了順序棧中的狀態(tài),棧空返回1,非空返回0

int  IsEmpty(SeqStack *s)

{

return s->top == 0;

}


//判斷是否棧滿,棧滿返回1,非滿返回0

int  IsFull(SeqStack *s )

{

return s->top >= s->capacity; 

}



//此函數(shù)解決,上面提到的,當基空間不夠時,增加空間。增加成功放回TRUE,也就是1,增加失敗返回FALSE

Status IncSize(SeqStack *s)

{

ElemType *newbase = (ElemType *)realloc(s->bace,sizeof(ElemType) * (s->capacity + STACK_INC_SIZE));

if(NULL == newbase)

{

printf("out of memory\n");

return FALSE;

}

s->bace = newbase;

s->capacity += STACK_INC_SIZE;

return TRUE;


}



順序棧的初始化


//初始化棧,就是為棧分配一個基空間,修改結構中top讓其等于0,表示空棧。

//初始化成功返回TRUE,初始化失敗返回FALSE;

Status Init_SeqStack(SeqStack *s)

{

s->bace = (ElemType *)malloc(sizeof(ElemType) * STACK_INIT_SIZE);

if(NULL == s->bace )

{

printf("out of memory,filed Init_SeqStack\n");

return FALSE;

}

s->top = 0;

s->capacity = STACK_INIT_SIZE;

return TRUE;

}



   順序棧的壓棧出棧


    這里一直強調棧是受限的線性表,那么,初始化,等其他的基本類同,主要區(qū)別就是在棧中的壓棧和出棧,壓棧出棧,對順序棧就是對順序表的尾插、尾刪,ok我們繼續(xù)用圖理一下結構。



數(shù)據(jù)結構之棧和隊列(C語言版)



//入棧(壓棧)成功返回TRUE,失敗返回FALSE

Status PushStack(SeqStack *s,ElemType x)

{


 /*

    這里就是解決棧的基空間不夠會滿的情況,需要調用前邊定義的函數(shù)再次為其增加空間。這里的 

    if條件需要注意,IsFull(s) 函數(shù),當棧已滿是返回1結果為真,IncSize(s)函數(shù)返回值,當開辟

    成功后返回1結果為真,所以當開辟成功后取反就為價所以&&操作后為假,if模塊就不執(zhí)

    行了,就繼續(xù)執(zhí)行后邊的代碼模塊

*/

if(IsFull(s) && !IncSize(s))            

{

printf("??臻g已滿,%d不能入棧\n",x);

return FALSE;

}

s->bace[s->top++] = x;

//s->top++;                         簡化代碼

return TRUE;

}


//出棧

Status  PopStack(SeqStack *s)

{

if(IsEmpty(s))

{

printf("棧已空,不能出棧\n");

return FALSE;

}

s->top--;

return TRUE;


}



    順序棧的獲取棧頂元素


    繼續(xù)看上一張圖,中的那段話,在C語言中,由于數(shù)組下標是從0開始的,top中保存棧中元素個數(shù),所以base[top]是指向棧頂元素的上一個空間,所以獲取棧頂元素時就需要減去1.


//獲取棧頂元素,

Status GetTop(SeqStack *s,ElemType *value)

{

if(IsEmpty(s))

{

printf("??臻g已空,不能取棧頂元素\n");

return FALSE;

}


*value = s->bace[s->top - 1];

return TRUE;

}



   順序棧的的長度


     top中保存的就是棧中的元素個數(shù),所以獲取棧中元素個數(shù)直接返回top的值就可以。

//獲取棧長度

int  GetLength(SeqStack s)

{

return s.top;

}



順序棧的清空棧


//清空棧,清空棧就是讓棧為空,所以只需要把top修改為0即可。

Status Clear(SeqStack *s)

{

s->top = 0;

return TRUE;

}



順序棧的摧毀棧



//摧毀棧,摧毀棧,意味著,不僅要top修改為0,更要釋放掉棧的空間。

Status Destory(SeqStack *s)

{

        free(s->bace);

s->bace = NULL;

s->top = 0;


return TRUE;

}



順序棧的輸出



//輸出棧中的元素,既然棧是從棧頂出,那么全部打印的輸出的時候,就需要從數(shù)組的尾輸出。

Status Show_Stack(SeqStack s)

{

if(s.top == 0)

{

printf("棧已空\n");

return FALSE;

}

for(int i = s.top-1; i >= 0; i--)

{

printf("%d",s.bace[i]);

}

printf("\n");

return TRUE;

}



順序棧的操作的基本操作就總結到這里,我們接著看棧的另一種存儲結構——鏈棧。



棧之鏈棧



    鏈棧也是棧,所以就不在介紹它的ADT,我們直接看鏈棧的結構。順序棧我們類比順序表,那么鏈棧我們類比鏈表,鏈表的種類那么多,我們選用那種呢?看棧的定義,我們不難得出是單鏈表,至于是帶頭結點還是不帶頭結點,由于棧操作是從棧頂操作也就是線性表的表尾,所以這里也不用考慮帶頭結點或者不帶頭結點,都一樣,也就不需要用帶頭結點了。

之前說順序棧的時候我們談到,棧會滿,我們采用了一種結構處理這種情況,為其增開辟空間,解決棧滿的情況。那么對于鏈棧,只要內(nèi)存不滿,就可以一直壓棧存儲元素。(系統(tǒng)內(nèi)存耗盡除外,當發(fā)生這種情況時,系統(tǒng)已經(jīng)癱瘓,所以程序的崩潰也已經(jīng)無所謂了。),所以也不需要順序棧中判斷棧是否為滿的情況這個函數(shù)。

ok我們繼續(xù)先看一下鏈棧的結構和結構定義:



棧的結構



數(shù)據(jù)結構之棧和隊列(C語言版)


#define ElemType int

#define TRUE     1

#define FALSE    0

#define Status   int


typedef struct StackNode

{

    ElemType data;

    struct StackNode *next;

}StackNode;


typedef StackNode* LinkStack;    // 這里需要注意后邊LinkStack  定義的變量就是指針類型的。

Status InitLinkStack(LinkStack *ls)

{

    *ls = NULL;

    return TRUE;

}



  鏈棧的壓棧出棧



  順序棧時已經(jīng)說過,壓棧出棧對于棧是最重要的結構,那么其他操作類比一下不帶頭結點單鏈表的操作,OK,壓棧就是對不帶頭結點的頭插,頭插!對就是頭插,不再是順序表中說的尾插,沒有搞錯,我們退回去看鏈棧的結構圖,當插入元素時,指向棧頂?shù)闹羔樦赶蛐碌脑?,那么當通過指向棧頂?shù)闹羔樤L問鏈表的時候,不就是后進先出,不就是符合棧的定義嗎?其實順序表也一定意義上頭插,我們通過棧頂看,不就是頭插嗎?說順序棧是尾插,是由于順序棧是通過數(shù)組實現(xiàn)的,數(shù)組名是指向數(shù)組的第一個元素的,我們要通過數(shù)組名操作存儲數(shù)據(jù),所以才說是尾插。我們繼續(xù)看結構圖:


數(shù)據(jù)結構之棧和隊列(C語言版)   


   結構弄清了鏈棧的壓棧只是一個沒有不帶頭結點的頭插;ok我們看具體C語言代碼實現(xiàn):


壓棧


//壓棧,壓棧成功返回TRUE,失敗返回FALSE; 

Status PushStack(LinkStack *ls,ElemType x)

{

StackNode  *s = (StackNode *)malloc(sizeof(StackNode));

if(NULL == s)

{

printf("out of memory\n");

return FALSE;

}

s->data = x; 


if(NULL == *ls)

{

*ls = s;

s->next = NULL;

}

else

{

s->next = *ls;

*ls = s;

}

return TRUE;


}


出棧


    對于鏈棧的出棧要注意釋放結點空間,防止內(nèi)存泄露


//出棧,出棧成功返回TRUE,失敗返回FALSE

Status PopLinkStack(LinkStack *ls)

{

StackNode *p = *ls;


if(NULL == p)

{

printf("棧已空,出棧失敗\n");

return FALSE;

}

*ls = p->next;

free(p);

p = NULL;

return TRUE;

}   



鏈棧的清空


    這里由于不帶頭結點,所以這里只給出了清空鏈棧的操作,但是,需要強調意義不一樣。


//清空鏈表,清空成功返回TRUE,失敗返回FALSE

Status ClearLinkStack(LinkStack *ls)

{

StackNode *p = *ls;

while(NULL != p)

{

*ls = p->next;

free(p);

p = *ls;

}

*ls = NULL;

return TRUE;

}



鏈棧的獲取棧頂元素


//獲取棧頂元素,即就是棧不空的條件下,獲取棧的首元素。

Status GetTop(LinkStack *ls,ElemType *value)

{

StackNode *p = *ls;

if(NULL == p)

{

printf("棧已空,獲取棧頂元素失敗\n");

return FALSE;

}


*value = p->data;

return TRUE;

}



鏈棧的獲取棧的長度


    鏈棧不像順序棧,通過top可以直接獲得棧中元素個數(shù),需要遍歷所以元素統(tǒng)計棧的元素個數(shù)。


//獲取鏈表長度

int  GetLength(LinkStack *ls)

{

StackNode *p = *ls;

int length = 0;

while(NULL != p)

{

length++;

p = p->next;

}

return p;

}



鏈棧的輸出


Status  Show_LinkStack(LinkStack *s)

{

StackNode *p = *s; 

if(NULL == s)

{

printf("棧已空\n");

return FALSE;

}

while(NULL != p->next)

{

printf("%d  ",p->data);

p = p->next;

}

printf("\n");

}



    ok棧的基本操作就總結到這里,寫道這里線性表,棧的基本操作我們都講完了,會有人說,堆,線性表就只能進行這些基本操作嗎?那豈不是很沒有用,下一篇我將對線性表的應用簡單總結一下。




向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI