資料結構與演算法學習--棧
阿新 • • 發佈:2018-12-15
1、順序棧的實現:
/************************************************************************* > File Name: arrayStack.h > Author: jinshaohui > Mail: [email protected] > Time: 18-10-12 > Desc: ************************************************************************/ #ifndef ARRAY_STACJ_H #define ARRAY_STACJ_H typedef struct _array_stack { int size;/*棧的大小*/ int pos;/*當前儲存元素的個數,即棧頂元素下表*/ int *array;/*資料儲存區*/ }stArrayStack; #define arrayStack_size(arrayStack) (arrayStack->size) #define arrayStack_is_empty(arrayStack) (arrayStack->pos == -1) #define arrayStack_is_full(arrayStack) (arrayStack->pos == (arrayStack->size-1)) #endif
/************************************************************************* > File Name: arrayStack.c > Author: jinshaohui > Mail: [email protected] > Time: 18-10-12 > Desc: 陣列實現順序棧 ************************************************************************/ #include<stdio.h> #include<stdlib.h> #include<string.h> #include"./arrayStack.h" /*建立並初始化順序棧*/ stArrayStack * arrayStack_create(int size) { stArrayStack *parrStack = NULL; parrStack = (stArrayStack *)malloc(sizeof(stArrayStack)); if (parrStack == NULL) { return NULL; } parrStack->size = size; parrStack->pos = -1; parrStack->array = (int *)malloc(sizeof(int)*size); if(parrStack->array == NULL) { free(parrStack); return NULL; } return parrStack; } /*銷燬順序棧*/ void arrayStack_destory(stArrayStack * parrStack) { if(parrStack == NULL) { return; } if (parrStack->array != NULL) { free(parrStack->array); } free(parrStack); return; } /*出棧*/ int arrayStack_pop(stArrayStack *parrStack) { int data = 0; if(arrayStack_is_empty(parrStack)) { return -1; } data = parrStack->array[parrStack->pos]; parrStack->pos--; return data; } /*入棧*/ int arrayStack_push(stArrayStack *parrStack,int data) { if(arrayStack_is_full(parrStack)) { return -1; } parrStack->pos++; parrStack->array[parrStack->pos] = data; return 0; } int arrayStack_push_new(stArrayStack*parrStack,int data) { int *ptmp = NULL; /*如果棧不滿,直接插入*/ if(!arrayStack_is_full(parrStack)) { return arrayStack_push(parrStack,data); } /*如果棧已經滿,申請記憶體*/ ptmp = (int *)malloc(2*parrStack->size*sizeof(int)); if (ptmp == NULL) { return -1; } memcpy(ptmp,parrStack->array,parrStack->size*sizeof(int)); free(parrStack->array); parrStack->array = ptmp; parrStack->size = 2*parrStack->size; parrStack->pos++; parrStack->array[parrStack->pos] = data; return ; } void arrayStack_dump(stArrayStack *parrStack) { int i = 0; if (arrayStack_is_empty(parrStack)) { printf("\r\n arrayStack is empty."); return; } printf("\r\narrayStack size = %d,pos= %d,", parrStack->size,parrStack->pos); for(i = 0; i <= parrStack->pos; i++) { printf("\r\narry[%d] = %d",i,parrStack->array[i]); } } int main() { int i = 0; int ret = 0; stArrayStack * parrStack = NULL; printf("\r\n create size = 4 arrayStack."); parrStack = arrayStack_create(4); if (parrStack == NULL) { printf("\r\n create size = 4 arrayStack faided."); return 0; } for (i = 0; i < 5; i++) { ret = arrayStack_push(parrStack,i); if(ret != 0) { printf("\r\n push size = %d arrayStack faided.",i); } } arrayStack_dump(parrStack); ret = arrayStack_push_new(parrStack,4); if(ret != 0) { printf("\r\n push size = %d arrayStack faided.",4); } arrayStack_dump(parrStack); arrayStack_destory(parrStack); return; }
2、鏈式棧的實現
棧的應用
1.棧在函式呼叫中的應用
作業系統給每個執行緒分配了一塊獨立的記憶體空間,這塊記憶體被組織成“棧”這種結構,用來儲存函式呼叫時的臨時變數。每進入一個函式,就會將其中的臨時變數作為棧幀入棧,當被呼叫函式執行完成,返回之後,將這個函式對應的棧幀出棧。
2.棧在表示式求值中的應用(比如:34+13*9+44-12/3)
利用兩個棧,其中一個用來儲存運算元,另一個用來儲存運算子。我們從左向右遍歷表示式,當遇到數字,我們就直接壓入運算元棧;當遇到運算子,就與運算子棧的棧頂元素進行比較,若比運算子棧頂元素優先順序高,就將當前運算子壓入棧,若比運算子棧頂元素的優先順序低或者相同,從運算子棧中取出棧頂運算子,從運算元棧頂取出2個運算元,然後進行計算,把計算完的結果壓入運算元棧,繼續比較。
3.棧在括號匹配中的應用(比如:{}{ ()})
用棧儲存為匹配的左括號,從左到右一次掃描字串,當掃描到左括號時,則將其壓入棧中;當掃描到右括號時,從棧頂取出一個左括號,如果能匹配上,則繼續掃描剩下的字串。如果掃描過程中,遇到不能配對的右括號,或者棧中沒有資料,則說明為非法格式。
當所有的括號都掃描完成之後,如果棧為空,則說明字串為合法格式;否則,說明未匹配的左括號為非法格式。
4.如何實現瀏覽器的前進後退功能?
我們使用兩個棧X和Y,我們把首次瀏覽的頁面依次壓如棧X,當點選後退按鈕時,再依次從棧X中出棧,並將出棧的資料一次放入Y棧。當點選前進按鈕時,我們依次從棧Y中取出資料,放入棧X中。當棧X中沒有資料時,說明沒有頁面可以繼續後退瀏覽了。當Y棧沒有資料,那就說明沒有頁面可以點選前進瀏覽了。
思考:
- 我們在講棧的應用時,講到用函式呼叫棧來儲存臨時變數,為什麼函式呼叫要用“棧”來儲存臨時變數呢?用其他資料結構不行嗎? 答:**因為函式呼叫的執行順序符合後進者先出,先進者後出的特點。**比如函式中的區域性變數的生命週期的長短是先定義的生命週期長,後定義的生命週期短;還有函式中呼叫函式也是這樣,先開始執行的函式只有等到內部呼叫的其他函式執行完畢,該函式才能執行結束。 正是由於函式呼叫的這些特點,根據資料結構是特定應用場景的抽象的原則,我們優先考慮棧結構。 2.我們都知道,JVM 記憶體管理中有個“堆疊”的概念。棧記憶體用來儲存區域性變數和方法呼叫,堆記憶體用來儲存 Java 中的物件。那 JVM 裡面的“棧”跟我們這裡說的“棧”是不是一回事呢?如果不是,那它為什麼又叫作“棧”呢? 答:JVM裡面的棧和我們這裡說的是一回事,被稱為方法棧。和前面函式呼叫的作用是一致的,用來儲存方法中的區域性變數。