以前學習的線性表以及線性表在物理結構中順序存儲和鏈式存儲兩種方式的代碼實現。今天咱們來學習一下一種特殊的線性表---棧,以及棧的順序存儲和鏈式存儲。node
棧是一種特殊的線性表,特殊點在於棧具備「先進後出」的一種形式。markdown
以前咱們在學習線性表的時候,實現過相關的插入和刪除方法。在線性表中,你能夠在任意的位置插入和刪除數據結點。而棧,只能在指定的位置插入和刪除數據結點,咱們一般叫這個指定位置爲「棧頂」。對應的另外一端,咱們叫「棧底」。數據結構
概念比較簡單,接下來咱們直接上代碼app
既然棧是線性表的一種,根據往期的學習,一下的代碼要分爲順序存儲和鏈式存儲兩種方式的實現函數
#include <stdio.h> #include <stdlib.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXSIZE 20 typedef int Status; typedef int SElemType; typedef struct Stack{ SElemType *data; int top; }SqStack; // init Status initStack(SqStack *stack) { stack->data = (SElemType*)malloc(sizeof(SElemType) * MAXSIZE); stack->top = -1; return OK; } // clear Status clearStack(SqStack *stack) { stack->top = -1; return OK; } // is empty Status isEmptyStack(SqStack stack) { if (stack.top == -1) { return TRUE; } else { return FALSE; } } // stack長度 int length(SqStack stack) { return stack.top + 1; } //獲取棧頂元素 Status getTopElem(SqStack stack, SElemType *elem) { if (stack.top == -1) { return ERROR; } else { *elem = stack.data[stack.top]; return OK; } } // 入棧 Status pushElem2Stack(SqStack *stack, SElemType elem) { if (stack->top == MAXSIZE - 1) { return ERROR; } stack->top ++; stack->data[stack->top] = elem; return OK; } // 出棧 Status popElemFromStack(SqStack *stack, SElemType *elem) { if (stack->top == -1) { return ERROR; } *elem = stack->data[stack->top]; stack->top--; return OK; } // 打印棧 Status displayStack(SqStack stack) { if (stack.top == -1) { return ERROR; } for (int i = 0; i <= stack.top; i++) { printf("%5d", stack.data[i]); } printf("\n"); return OK; } 複製代碼
#include <stdio.h> #include <stdlib.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int Status; typedef int ElemType; typedef struct StackNode{ ElemType data; struct StackNode *next; }StackNode, *StackNodePtr; typedef struct LinkStack{ StackNodePtr top; int count; }LinkStack; //init Status initStack(LinkStack *stack) { stack->top = NULL; stack->count = 0; return OK; } //clear Status clearStack(LinkStack *stack) { StackNodePtr p = stack->top; StackNodePtr q; while (p) { q = p->next; free(p); p = q; } stack->top = NULL; stack->count = 0; return OK; } //isEmpty Status isEmptyStack(LinkStack stack) { if (stack.count == 0) { return TRUE; } else { return FALSE; } } //length int length(LinkStack stack) { return stack.count; } //getTopElem Status getTopElem(LinkStack stack, ElemType *elem) { if (stack.top == NULL) { return ERROR; } *elem = stack.top->data; return OK; } //pushElem2Stack Status pushElem2Stack(LinkStack *stack, ElemType elem) { StackNodePtr node = (StackNodePtr)malloc(sizeof(StackNode)); if (node == NULL) { return ERROR; } node->data = elem; node->next = stack->top; stack->top = node; stack->count ++; return OK; } //popElemFromStack Status popElemFromStack(LinkStack *stack, ElemType *elem) { if (stack->top == NULL) { return ERROR; } StackNodePtr p = stack->top; *elem = p->data; stack->top = stack->top->next; free(p); stack->count --; return OK; } //displayStack Status displayStack(LinkStack stack) { StackNodePtr p = stack.top; while (p) { printf("%5d", p->data); p = p->next; } printf("\n"); return OK; } 複製代碼
在咱們平時開發中,不多直接使用棧。可是棧又是相對比較基礎的數據結構。學習
不少時候系統會給咱們封裝提供以棧的方式實現的模塊,例如iOS中的UINavigationViewController,可能一些非客戶端app的開發者不知道這個東西。它就是從一個界面中,點擊一個按鈕,進入到另外一個界面,至關於棧的入棧;當你點擊返回的時候,回到前一個界面,至關於出棧。spa
其實,還有一個咱們更經常使用的,並且每一個人都會用的,但平時不會太多去注意到的一個點,就是函數調用。在高級語言的程序中,調用函數和被調用函數之間的連接和信息交換都是經過棧的形式來實現的。指針
在一個函數(如下用「調用函數」表示)內,調用另外一個函數(如下用「被調用函數」表示),在運行被調用函數以前,系統會先完成下面的幾件事code
從被調用函數返回到調用函數以前,系統一樣會作一些事情orm
當多個函數構成嵌套條用時,按照「先調用後返回」方式,也就是先進後出的棧的形式實現的。
好比代碼實現:
int second(int d){ int x,y; //... } int first(int s ,int t){ int i; //... second(i)//2.⼊入棧 //... } void main( ){ int m,n; first(m ,n); //1.⼊入棧 //... } 複製代碼
當main函數中,調用了first函數,main函數就是調用函數,first函數就是被調用函數。方法調用的棧結構如圖:
遞歸函數運行過程相似多個函數嵌套調用,只是調用函數和被調用函數是同一個函數。
爲了保證遞歸函數的正確調用,系統須要設立一個「遞歸工做棧」做爲整個遞歸函數運行期間的數據存儲區。每一層遞歸所需信息構成一個工做記錄,其中包括全部的實參,全部的局部變量以及上一層的返回地址。每進入一層遞歸,就產生一個新的工做記錄壓入棧頂。沒退出一個遞歸,就從棧頂彈出一個記錄。
在一個函數,過程或數據結構敵營的內部又直接或間接出現定義自己的應用;則稱爲他們是遞歸的或者是遞歸定義。
在下面3中狀況下,咱們會使用到遞歸來解決問題
階乘
// Fact(n) // 若n=0, 則返回1; // 若n > 1, 則返回n*Fact(n-1); long Fact(Long n){ if(n=0) return -1; else return n * Fact(n-1); } 複製代碼
斐波拉契數列
//Fib(n) // 若n=1或者n=2,則返回1; // 若n > 2, 則Fib(n-1) + Fib(n-2); long Fib(Long n){ if(n == 1 || n == 2) return 1; else return Fib(n-1)+Fib(n-2); } 複製代碼
void TraverseList(LinkList p){ if(p == NULL) return; else{ printf("%d",p->data); p TraverseList(p->next); } } 複製代碼
有一類問題,雖然問題自己並無明顯的遞歸結構,可是採起遞歸求解比迭代更簡單,如Hanoi塔問題、八皇后問題、迷宮問題
//漢諾塔 Hanoi void Hanoi(int n, char a, char b, char c) { if (n == 1) { moves(a, 1, c); } else { Hanoi(n-1, a, c, b); moves(a, n, c); Hanoi(n-1, b, a, c); } } int main(int argc, const char * argv[]) { // insert code here... printf("Hello, Hanoi!\n"); Hanoi(4, 'a', 'b', 'c'); return 0; } 複製代碼
這節課的學習是在原有的基礎上擴展出來的,可是注意到了平時不注意的點(函數調用是棧的邏輯)。但願以上的總結,能夠給你帶來幫組。記住:沿途的風景要比目的地更彎的否!!!