1. 程式人生 > >二叉樹非遞迴實現

二叉樹非遞迴實現

二叉樹非遞迴實現會比較難理解一點,不過只要理解了非遞迴的,那麼遞迴的就非常好理解了。接下來進行圖文詳解。

C程式碼下載
C++程式碼下載
java程式碼下載
( 備用地址下載)

導航
1.建立二叉樹
2.前序遍歷二叉樹
3.中序遍歷二叉樹
4.後序遍歷二叉樹
5.層次遍歷二叉樹
6.計算二叉樹深度
7.計算二叉樹雙分支節點數
8.計算二叉樹單分支節點數
9.計算二叉樹葉子節點數
10.新增節點
11.查詢二叉樹中的節點

注:有一些重複的程式碼且多的就不重複貼出來了,需要的可以點上面的連結去下載。







一、建立二叉樹

按照前序遍歷來建立,給定一個串,其規則是空格代表空節點
例如給定串:ABC D EF G ;
建立步驟如下:

1.C程式碼

/*
* function              建立二叉樹(前序建立)
* param     char* s     根據給定字串建立   
* return                返回二叉樹的根節點
*/
PBTree CreateBTree(char* s)
{
    if (!s || s[0] == '\0')  //如果s為空 則樹也為空
        return NULL;
    Stack stack = CreateStack();  //建立一個棧
int i = 0; // 1.先建立根節點 PBTree root = CreateNode(s[i++]); PBTree btree = root; //用一個臨時的指標代替root,因為最後要返回root指標,所以它不能改變 Push(&stack,root); //將根節點壓入棧中 int len = _csize(s); while (i < len) { // 2.如果當前讀到的字元不為空,並且當前節點的左孩子不存在,則建立它 if (s[i] != ' '
&& btree->bLCExits == false) { btree->left = CreateNode(s[i]); //建立左孩子 btree->bLCExits = true; //左孩子存在 btree = btree->left; Push(&stack, btree); //入棧 ++i; } // 3.如果當前讀到的字元不為空,並且當前節點的右孩子不存在,則建立它 else if (s[i] != ' ' && btree->bRCExits == false) { btree->right = CreateNode(s[i]);//建立右孩子 btree->bRCExits = true; //右孩子存在 btree = btree->right; Push(&stack, btree); //入棧 ++i; } // 4.如果當前讀到的字元為空,並且當前節點的左孩子不存在,則將當前節點的左孩子置為存在(空也算孩子) else if (s[i] == ' ' && btree->bLCExits == false) //空節點 btree->bLCExits = true, ++i; //左孩子存在 // 5.如果當前讀到的字元為空,並且當前節點的右孩子不存在,則將當前節點的右孩子置為存在(空也算孩子) else if (s[i] == ' ' && btree->bRCExits == false) btree->bRCExits = true, ++i; //右孩子存在 // 6.回溯回去,當該節點的度為2的時候(就是左右孩子都存在的情況) if (btree->bLCExits && btree->bRCExits) Pop(&stack),btree = Top(&stack); } DestroyStack(&stack); //最後銷燬棧 return root; }

2.C++程式碼

/*
* function              建立二叉樹(前序建立)
* param     char* s     根據給定串建立
* return                無
*/
template<typename T>
void NonRecursionBTree<T>::Create(string s)
{
    if (s.empty())  //如果s為空 則樹也為空
        return ;
    stack<Node*> st;    //建立一個棧
    int i = 0;
    // 1.先建立根節點
    m_root = new Node(s[i++]);
    Node* btree = m_root;       //用一個臨時的指標代替root,因為最後要返回root指標,所以它不能改變
    st.push(btree);             //將根節點壓入棧中
    int len = s.size();
    while (i < len)
    {
        // 2.如果當前讀到的字元不為空,並且當前節點的左孩子不存在,則建立它
        if (s[i] != ' ' && btree->bLCExits == false)
        {
            btree->left = new Node(s[i]);   //建立左孩子
            btree->bLCExits = true;         //左孩子存在
            btree = btree->left;
            st.push(btree);  //入棧
            ++i;
        }
        // 3.如果當前讀到的字元不為空,並且當前節點的右孩子不存在,則建立它
        else if (s[i] != ' ' && btree->bRCExits == false)
        {
            btree->right = new Node(s[i]);  //建立右孩子
            btree->bRCExits = true;         //右孩子存在
            btree = btree->right;
            st.push(btree);  //入棧
            ++i;
        }
        // 4.如果當前讀到的字元為空,並且當前節點的左孩子不存在,則將當前節點的左孩子置為存在(空也算孩子)
        else if (s[i] == ' ' && btree->bLCExits == false) //空節點
            btree->bLCExits = true, ++i;  //左孩子存在
        // 5.如果當前讀到的字元為空,並且當前節點的右孩子不存在,則將當前節點的右孩子置為存在(空也算孩子)
        else if (s[i] == ' ' && btree->bRCExits == false)
            btree->bRCExits = true, ++i;  //右孩子存在       

        // 6.回溯回去,當該節點的度為2的時候(就是左右孩子都存在的情況)
        if (btree->bLCExits && btree->bRCExits)
            st.pop(), btree = st.top();
    }
    while (!st.empty())  //最後銷燬棧
        st.pop();
}

3.java程式碼

/*
    * function              建立二叉樹(前序建立)
    * param     String s    根據給定串建立
    * return                無
    */
    public void create(String s) {
        if (s.isEmpty())  //如果s為空 則樹也為空
            return ;
        Stack<Node> st = new Stack<Node>(); //建立一個棧
        int i = 0;
        // 1.先建立根節點
        root = new Node(s.charAt(i++));
        Node btree = root;      //用一個臨時的指標代替root,因為最後要返回root指標,所以它不能改變
        st.push(btree);             //將根節點壓入棧中
        int len = s.length();
        while (i < len) {
            // 2.如果當前讀到的字元不為空,並且當前節點的左孩子不存在,則建立它
            if (s.charAt(i) != ' ' && btree.bLCExits == false) {
                btree.left = new Node(s.charAt(i)); //建立左孩子
                btree.bLCExits = true;          //左孩子存在
                btree = btree.left;
                st.push(btree);  //入棧
                ++i;
            }
            // 3.如果當前讀到的字元不為空,並且當前節點的右孩子不存在,則建立它
            else if (s.charAt(i) != ' ' && btree.bRCExits == false) {
                btree.right = new Node(s.charAt(i));    //建立右孩子
                btree.bRCExits = true;          //右孩子存在
                btree = btree.right;
                st.push(btree);  //入棧
                ++i;
            }
            // 4.如果當前讀到的字元為空,並且當前節點的左孩子不存在,則將當前節點的左孩子置為存在(空也算孩子)
            else if (s.charAt(i) == ' ' && btree.bLCExits == false) {//空節點
                btree.bLCExits = true;
                ++i;  //左孩子存在
            }   
            // 5.如果當前讀到的字元為空,並且當前節點的右孩子不存在,則將當前節點的右孩子置為存在(空也算孩子)
            else if (s.charAt(i) == ' ' && btree.bRCExits == false) {
                btree.bRCExits = true;
                ++i;  //右孩子存在       
            }

            // 6.回溯回去,當該節點的度為2的時候(就是左右孩子都存在的情況)
            if (btree.bLCExits && btree.bRCExits) {
                st.pop();
                btree = st.peek();
            }

        }
        st.clear(); //最後銷燬棧
    }

1).為什麼要用兩個標誌位bLCExits和bRCExits?
這樣子比較容易找到回溯點,就是當左右兩個孩子都存在了就回溯,不能根據是否為空來判斷是否回溯。






二、前序遍歷二叉樹
參考出處,點選跳轉

遍歷順序是:根節點 -> 左節點 -> 右節點
然後一個二叉樹又可以分為很多子樹,每一顆子樹都會有根、左、右節點。

/*
* function              前序遍歷
* param     PBTree      root
* return                無
*/
void PreOrder(PBTree root)
{
    Stack stack = CreateStack();   //建立一個棧
    PBTree btree = root;           //建立臨時指標指向root
    // 1. 若當前節點不為空,或者棧中元素不為空(相當於還沒有遍歷完所有節點)
    while (btree || !StackIsEmpty(&stack))
    {
        // 2. 先遍歷左子樹,一直到左子樹為空為止。
        while (btree)
        {
            printf("%c", btree->data);
            Push(&stack,btree);
            btree = btree->left;
        }

        // 3.若棧中還有元素,則將當前btree賦值為它的右子樹,然後再重複 1~2步驟
        if (!StackIsEmpty(&stack))
        {
            btree = Top(&stack);
            Pop(&stack);         
            btree = btree->right;
        }
    }
    printf("\n");
}





三、中序遍歷二叉樹
遍歷順序是:左節點 -> 根節點 -> 右節點

/*
* function              中序遍歷
* param     PBTree      root
* return                無
*/
void InOrder(PBTree root)
{
    Stack stack = CreateStack();   //建立一個棧
    PBTree btree = root;           //建立臨時指標指向root
    // 1. 若當前節點不為空,或者棧中元素不為空(相當於還沒有遍歷完所有節點)
    while (btree || !StackIsEmpty(&stack))
    {
        // 2. 先遍歷左子樹,一直到左子樹為空為止。
        while (btree)
        {   
            Push(&stack, btree);
            btree = btree->left;
        }

        // 3.若棧中還有元素,則將當前btree賦值為它的右子樹,然後再重複 1~2步驟
        if (!StackIsEmpty(&stack))
        {
            btree = Top(&stack);
            printf("%c", btree->data);   //遍歷完左子樹後輸出它們的根節點
            Pop(&stack);
            btree = btree->right;
        }
    }
    printf("\n");
}





四、後序遍歷二叉樹
遍歷順序:左節點 -> 右節點 -> 根節點

/*
* function              後序遍歷
* param     PBTree      root
* return                無
*/
void PostOrder(PBTree root)
{
    Stack stack = CreateStack();    //建立一個棧
    PBTree cur;                     //當前結點 
    PBTree pre = NULL;              //前一次訪問的結點 
    Push(&stack,root);              //先將根節點入棧
    while (!StackIsEmpty(&stack))
    {
        cur = Top(&stack);          //這裡的cur就像是每顆子樹的根節點,然後重複這些步驟
        if ((!cur->left && !cur->right) ||
            (pre && (pre == cur->left || pre == cur->right)))
        {
            printf("%c", cur->data);  //如果當前結點沒有孩子結點或者孩子節點都已被訪問過 
            Pop(&stack);
            pre = cur;
        }
        else
        {
            if (cur->right != NULL)      //這裡先將右孩子入棧,這樣出棧的時候,左孩子就先出棧
                Push(&stack,cur->right);
            if (cur->left != NULL)
                Push(&stack,cur->left);
        }
    }
    printf("\n");
}





五、層次遍歷二叉樹

層次遍歷就簡單了,這個要用到佇列
步驟為:
1.將根節點入隊
2.將當前節點置為隊頭節點 (cur = front)
3.出隊
4.訪問當前節點
5.如果當前節點的左孩子不為空,左孩子入隊
6.如果當前節點的右孩子不為空,有孩子入隊
7.重複2~6步驟即可

這個就相當於訪問上一層的節點的時候,將下一層的結點依次入隊以待訪問

/*
* function              層次遍歷
* param     PBTree      root
* return                無
*/
void translevel(PBTree root)
{
    if (!root) return;
    Queue queue = CreateQueue();        //建立一個佇列
    PBTree cur = root;                  //當前節點
    QPush(&queue, root);                //先將根節點加入佇列中
    while (!QueueIsEmpty(&queue))       //當佇列中沒有元素時,遍歷完成 
    {
        cur = Front(&queue);            //獲取當前隊頭元素
        QPop(&queue);                   //遍歷過後就出隊
        printf("%c", cur->data);        //先輸出該子樹的根節點
        if (cur->left)                  //如果左孩子不為空,則入隊等待遍歷
            QPush(&queue, cur->left);   
        if (cur->right)                 //如果右孩子不為空,則入隊等待遍歷
            QPush(&queue, cur->right);
    }
    printf("\n");
}





六、計算二叉樹深度
利用層次遍歷來求,遍歷的最大層數即深度

/*
* function              計算二叉樹深度
* param     PBTree      root
* return                無
*/
int BTreeDepth(PBTree root)
{
    if (!root) return 0;
    Queue queue = CreateQueue();        //建立一個佇列
    PBTree cur = root;                  //當前節點
    int iDepth = 0;
    QPush(&queue, root);                    //先將根節點加入佇列中
    while (!QueueIsEmpty(&queue))           //當佇列中沒有元素時,遍歷完成 
    {
        ++iDepth;                           //外層迴圈控制層數
        int curLevelNodes = queue.count;    //當前層數的節點數
        int temp = 0;                       //臨時記錄已經遍歷的節點數

        while (temp++ < curLevelNodes)      //當遍歷完當前層數後,退出內層迴圈,繼續遍歷下一層
        {
            cur = Front(&queue);            //獲取當前隊頭元素
            QPop(&queue);                   //遍歷過後就出隊
            if (cur->left)                  //如果左孩子不為空,則入隊等待遍歷
                QPush(&queue, cur->left);

            if (cur->right)                 //如果右孩子不為空,則入隊等待遍歷
                QPush(&queue, cur->right);
        }
    }
    return iDepth;
}





七、計算二叉樹雙分支節點數
利用前序遍歷來求

/*
* function              計算二叉樹雙分支節點數
* param     PBTree      root
* return                無
*/
int GetN2(PBTree root)
{
    Stack stack = CreateStack();   //建立一個棧
    PBTree btree = root;           //建立臨時指標指向root
    int count = 0;
    //利用前序遍歷來訪問所有的節點
    while (btree || !StackIsEmpty(&stack))
    {   
        while (btree)
        {
            Push(&stack, btree);
            btree = btree->left;
        }
        if (!StackIsEmpty(&stack))
        {
            btree = Top(&stack);
            if (btree)                            //再這裡能保證所有的節點都能被遍歷到
                if (btree->left && btree->right)  //當該節點有兩個分支的時候+1
                    ++count;
            Pop(&stack);
            btree = btree->right;
        }
    }
    return count;
}





八、計算二叉樹單分支節點數
和計算雙分支節點的方法一樣,只需要把判斷語句改一下即可

if (btree)                            //再這裡能保證所有的節點都能被遍歷到
                if ((btree->left && !btree->right) || (!btree->left && btree->right))  //當該節點僅且只有一個分支的時候+1
                    ++count;





九、計算二叉樹葉子節點數
這個就簡單了,有一個公式: n0 = n2 + 1

/*
* function              計算二叉樹終端節點數
* param     PBTree      root
* return                無
*/
int GetN0(PBTree root)
{
    return GetN2(root) + 1; //計算公式n0 = n2 + 1;
}





十、新增節點
可以用前序、中序、後序、層次遍歷的方法來新增,前三個的缺點很明顯,最後新增後可能會退化成一個長長的單鏈表。所以這裡採用層次遍歷的方法新增,一層層掃描,遇到空節點就新增在它那裡。

/*
* function              新增節點值(新增的位置是不確定的)
* param     PBTree      root
* param     char        ch
* return                無
*/
void AddValue(PBTree root, char ch)
{
    //採用層次遍歷的辦法,一層層掃描,若有空的地方,則新增到該地方
    if (!root)
    {
        root = CreateNode(ch);
        return;
    }   
    Queue queue = CreateQueue();        //建立一個佇列
    PBTree cur = root;                  //當前節點
    QPush(&queue, root);                //先將根節點加入佇列中
    while (!QueueIsEmpty(&queue))       //當佇列中沒有元素時,遍歷完成 
    {
        cur = Front(&queue);            //獲取當前隊頭元素
        QPop(&queue);                   //遍歷過後就出隊
        if (cur->left)                  //如果左孩子不為空,則入隊等待遍歷
            QPush(&queue, cur->left);
        else                            //否則就新增值,然後退出
        {
            cur->left = CreateNode(ch);
            break;
        }
        if (cur->right)                 //如果右孩子不為空,則入隊等待遍歷
            QPush(&queue, cur->right);
        else                            //否則就新增值,然後退出
        {
            cur->right = CreateNode(ch);//否則就新增值,然後退出
            break;
        }
    }
}

十一、查詢二叉樹中的節點

用層次遍歷的方法查詢

/*
* function              查詢值
* param     PBTree      root
* param     char        ch
* return                成功返回true,否則返回false
*/
bool Search(PBTree root, char ch)
{
    //利用層次遍歷來查詢
    if (!root) return false;
    Queue queue = CreateQueue();        //建立一個佇列
    PBTree cur = root;                  //當前節點
    QPush(&queue, root);                //先將根節點加入佇列中
    while (!QueueIsEmpty(&queue))       //當佇列中沒有元素時,遍歷完成 
    {
        cur = Front(&queue);            //獲取當前隊頭元素
        if (cur->data == ch)
            return true;
        QPop(&queue);                   //遍歷過後就出隊
        if (cur->left)                  //如果左孩子不為空,則入隊等待遍歷
            QPush(&queue, cur->left);
        if (cur->right)                 //如果右孩子不為空,則入隊等待遍歷
            QPush(&queue, cur->right);
    }
    return false;
}