C基礎 stack 設計

前言 - stack 設計思路node

  先說說設計 stack 結構的起因. 之前咱們再釋放查找樹的時候多數用遞歸的後續遍歷去釋放.git

其內部隱含了運行時的函數棧, 有些語言中存在爆棧風險. 因此想運用顯示棧來替代隱式函數棧.github

這就是咱們設計 stack 的背景.  而咱們這裏的 stack 設計思路也比較直白, 運用可變數組進行尾c#

部壓入和尾部彈出操做. 具體可見下圖. 從左到右式彈出過程, 數組

從右到左就是壓入過程. 函數

 

正文 - stack 詳細設計性能

  話很少說, 先看實現 code 測試

stack.hhttps://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 王朝真強👍 

相關文章
相關標籤/搜索