前言 - stack 設計思路node
先說說設計 stack 結構的起因. 之前咱們再釋放查找樹的時候多數用遞歸的後續遍歷去釋放.git
其內部隱含了運行時的函數棧, 有些語言中存在爆棧風險. 因此想運用顯示棧來替代隱式函數棧.github
這就是咱們設計 stack 的背景. 而咱們這裏的 stack 設計思路也比較直白, 運用可變數組進行尾c#
部壓入和尾部彈出操做. 具體可見下圖. 從左到右式彈出過程, 數組
從右到左就是壓入過程. 函數
正文 - stack 詳細設計性能
話很少說, 先看實現 code 測試
stack.h - https://github.com/wangzhione/structc/blob/master/structc/struct/stack.h優化
#ifndef _STACK_H #define _STACK_H #include "struct.h" #define INT_STACK (1 << 8) // // struct stack 對象棧 // stack empty <=> tail = -1 // stack full <=> tail == cap // struct stack { int tail; // 尾結點 int cap; // 棧容量 void ** data; // 棧實體 }; // // stack_init - 初始化 stack 對象棧 // stack_free - 清除掉 stack 對象棧 // return : void // inline void stack_init(struct stack * s) { assert(s && INT_STACK > 0); s->tail = -1; s->cap = INT_STACK; s->data = malloc(sizeof(void *) * INT_STACK); } inline void stack_free(struct stack * s) { free(s->data); } // // stack_delete - 刪除 stack 對象棧 // s : stack 對象棧 // fdie : node_f push 結點刪除行爲 // return : void // inline void stack_delete(struct stack * s, node_f fdie) { if (s) { if (fdie) { while (s->tail >= 0) fdie(s->data[s->tail--]); } stack_free(s); } } // // stack_empty - 判斷 stack 對象棧是否 empty // s : stack 對象棧 // return : true 表示 empty // inline bool stack_empty(struct stack * s) { return s->tail < 0; } // // stack_top - 獲取 stack 棧頂對象 // s : stack 對象棧 // return : 棧頂對象 // inline void * stack_top(struct stack * s) { return s->tail >= 0 ? s->data[s->tail] : NULL; } // // stack_pop - 彈出棧頂元素 // s : stack 對象棧 // return : void // inline stack_pop(struct stack * s) { if (s->tail >= 0) --s->tail; } // // stack_push - 壓入元素到對象棧棧頂 // s : stack 對象棧 // m : 待壓入的對象 // return : void // inline void stack_push(struct stack * s, void * m) { if (s->cap <= s->tail) { s->cap <<= 1; s->data = realloc(s->data, sizeof(void *) * s->cap); } s->data[++s->tail] = m; } #endif//_STACK_H
INT_STACK 是拍腦門搞得 8 x 8, 惟一的損耗點可能在 stack_top 和 stack_empty 配合的時候, 須要spa
冗餘判斷一步 tail >= 0. 不過隨着條件的分支預測, 實際影響不大, 也還好. 咱們不妨寫個業務測試.
#include <stack.h> void stack_test(void) { struct stack s; stack_init(&s); char * str = NULL; stack_push(&s, ++str); stack_push(&s, ++str); stack_push(&s, ++str); // 數據輸出 for (char * now; (now = stack_top(&s)); stack_pop(&s)) printf("now = %p\n", now); stack_push(&s, ++str); stack_push(&s, ++str); for (char * now; (now = stack_top(&s)); stack_pop(&s)) printf("now = %p\n", now); stack_free(&s); }
那最終看 stack 實際運用場景吧, 運用顯示棧來銷燬查找樹
// rtree_die - 後序刪除樹結點 static void rtree_die(struct $rtree * root, node_f fdie) { struct $rtree * pre = NULL; struct stack s; stack_init(&s); stack_push(&s, root); do { struct $rtree * cur = stack_top(&s); if ((!cur->left && !cur->right) || ((cur->left == pre || cur->right == pre) && pre)) { fdie(pre = cur); stack_pop(&s); } else { if (cur->right) stack_push(&s, cur->right); if (cur->left) stack_push(&s, cur->left); } } while (!stack_empty(&s)); stack_free(&s); }
更多細節代碼能夠閱讀 rtree.h 對於二叉樹後續非遞歸遍歷, 壓入右子樹, 左子樹,對比上次彈出的結點 ...
後記 - stack 將來展望
Friend - https://music.163.com/#/song?id=523560
錯誤是不免的, 歡迎交流提高 ~
基於當前 stack 設計, 將來展望具體從兩方面處理. 複雜方面, 能夠優化一下內存相關操做, 初始化,
擴容, 縮容等. 簡單方面, 你們也看出來了, 這個棧代碼極其少, 純追求性能均可以直接放棄封裝, 內嵌到
須要使用的地方和大業務混爲一體. 那今天就到這裏了, 2019/08/25 21:50 Dota2 OG 王朝真強👍