【資料結構】二叉樹的建立與遍歷

Owen_csc發表於2020-11-22

演算法設計思路

二叉樹的建立採用struct指標建立結點,每個結點內建兩個struct指標來指向它的左右孩子,其中建立方法可採用先序,中序、後序。這裡僅介紹先序建樹,中序建樹和後序建樹實際上殊途同歸。
建樹程式碼如下

void build(Btree &t)
/**
 * @description: 建樹
 * 採用先序建樹,先根後左右
 * @param {*Btree &t}
 */
{
    char s;
    sc("%c", &s);
    if (isdigit(s))
    {
        t = new btree;
        t->data = s;
        build(t->l);
        build(t->r);
    }
    else
        t = NULL;
}

這邊先簡單敘述一下二叉樹的先序、中序、後序遍歷的遞迴方法。首先對於先序遍歷,採用根左右的順序輸出,所以每遇到一個結點就先輸出它對應的值,再先遞迴它的左孩子然後遞迴它的右孩子。
程式碼如下

void pre_rec(Btree t)
/**
 * @description: 遞迴先序遍歷二叉樹
 * @param {*Btree t}
 */
{
    if (t != NULL)
    {
        pr("%c ", t->data);
        pre_rec(t->l);
        pre_rec(t->r);
    }
}
void in_rec(Btree t)
/**
 * @description: 遞迴中序遍歷二叉樹
 * @param {*Btree t}
 */
{
    if (t != NULL)
    {
        in_rec(t->l);
        pr("%c ", t->data);
        in_rec(t->r);
    }
}
void post_rec(Btree t)
/**
 * @description: 遞迴後序遍歷二叉樹
 * @param {*Btree t}
 */
{
    if (t != NULL)
    {
        post_rec(t->l);
        post_rec(t->r);
        pr("%c ", t->data);
    }
}

先序遍歷的非遞迴方法: 從根結點開始往左孩子方向遍歷,每遍歷到一個元素就把它對應的值輸出,然後將該結點入棧,直至左孩子為空,然後彈出棧頂結點,遍歷其右子樹,如此往復直至棧空且結點為空。

void pre_transe(Btree ti)
/**
 * @description: 非遞迴先序遍歷二叉樹
 * 從根結點開始往左孩子方向遍歷,每遍歷到一個元素就
 * 把它對應的值輸出,然後將該結點入棧,直至左孩子為
 * 空,然後彈出棧頂結點,遍歷其右子樹,如此往復直至
 * 棧空且結點為空。
 * @param {*Btree ti}
 */
{
    stack<Btree> s;
    while (ti != NULL || !s.empty())
    {
        while (ti != NULL)
        {
            pr("%c ", ti->data);
            s.push(ti);
            ti = ti->l;
        }
        if (!s.empty())
        {
            ti = s.top()->r;
            s.pop();
        }
    }
}

非遞迴中序遍歷二叉樹 從根結點開始往左孩子方向遍歷,然後將該結點入棧,直至左孩子為空,然後彈出棧頂結點並輸出該結點對應的元素的值,接著遍歷其右子樹如此往復直至棧空且結點為空。

void in_transe(Btree ti)
/**
 * @description: 非遞迴中序遍歷二叉樹
 * 從根結點開始往左孩子方向遍歷,然後將該結點入棧,
 * 直至左孩子為空,然後彈出棧頂結點並輸出該結點對
 * 應的元素的值,接著遍歷其右子樹如此往復直至棧空
 * 且結點為空。
 * @param {*Btree ti}
 */
{
    stack<Btree> s;
    while (ti != NULL || !s.empty())
    {
        while (ti != NULL)
        {
            s.push(ti);
            ti = ti->l;
        }
        if (!s.empty())
        {
            ti = s.top();
            s.pop();
            pr("%c ", ti->data);
            ti = ti->r;
        }
    }
}

非遞迴後序遍歷二叉樹從根節點開始順序遍歷其左孩子,並將每個順序遍歷到的左孩子入棧同時標記其入棧次數,當左孩子為空 時,彈出棧頂元素,判斷棧頂元素入棧次數,若為2則輸出棧頂元素對應的值,並將棧頂結點置為空,避免陷入死迴圈,若為1,則將該節點再次入棧,並自增其入棧次數,然後遍歷其右子樹,如此往復,直至棧空且結點為空。

void post_transe(Btree ti)
/**
 * @description: 非遞迴後序遍歷二叉樹
 * 從根節點開始順序遍歷其左孩子,並將每個順序遍歷
 * 到的左孩子入棧同時標記其入棧次數,當左孩子為空
 * 時,彈出棧頂元素,判斷棧頂元素入棧次數,若為2則
 * 輸出棧頂元素對應的值,並將棧頂結點置為空,避免
 * 陷入死迴圈,若為1,則將該節點再次入棧,並自增其
 * 入棧次數,然後遍歷其右子樹,如此往復,直至棧空
 * 且結點為空。
 * @param {*Btree ti}
 */
{
    stack<Btree> s;
    while (ti != NULL || !s.empty())
    {
        while (ti != NULL)
        {
            ti->visit = 1;
            s.push(ti);
            ti = ti->l;
        }
        if (!s.empty())
        {
            ti = s.top();
            s.pop();
            if(ti->visit == 1)
            {
                ti->visit++;
                s.push(ti);
                ti = ti->r;
            }
            else
            {
                if(ti->visit == 2)
                {
                    pr("%c ", ti->data);
                    ti = NULL;
                }
            }
        }
    }
}

層序遍歷二叉樹將根節點入隊後,逐一彈出隊首元素輸出它對應的值並將隊首元素的左右孩子入隊,如此往復,直至隊空

void level_transe(Btree t)
/**
 * @description: 層序遍歷二叉樹
 * 將根節點入隊後,逐一彈出隊首元素輸出它對應的值
 * 並將隊首元素的左右孩子入隊,如此往復,直至隊空
 * @param {*Btree t}
 */
{
    queue<btree> q;
    if (t != NULL)
    {
        q.push(*t);
        btree ti;
        while (!q.empty())
        {
            ti = q.front();
            q.pop();
            pr("%c ", ti.data);
            if (ti.l != NULL)
                q.push(*ti.l);
            if (ti.r != NULL)
                q.push(*ti.r);
        }
    }
}

相關文章