非遞歸實現二叉樹先序、中序、后序遍歷(棧實現)


本篇文章主要詳解利用棧的方式二叉樹先序、中序、后序遍歷的非遞歸寫法

首先我們需要實現一顆二叉樹。

以下是通過先序序列建樹的代碼

例如:先序序列{1,2,4,10,'#','#',6,11,'#','#',7'#','#','#',3,8,'#','#',9}代表以下的樹


    typedef struct Node
    {
        int _data;
        Node* _right;
        Node* _left;
        Node()
        {}
        Node(int x)
        {
            _data = x;
            _left = NULL;
            _right = NULL;
        }
    }*pNode;
     

//參數類型arr 數組指針,index 下標,size 數組元素個數,invalid代表先序遍歷時遇到的NULL

pNode createTree_PrevOrder( const int* arr, size_t& index,const size_t size,int invalid){Node* root = NULL;if (index < size && arr[index] != invalid){root = new Node(arr[index]);root->_left = createTree_PrevOrder(arr, ++index, size, invalid);root->_right = createTree_PrevOrder(arr, ++index, size, invalid);}return root;}

先序非遞歸:

先序遍歷時,每當我們壓入一個結點,我們壓入結點前對其進行訪問。

    void prevOrder_NR(pNode root)
    {
        if (NULL == root)
            cout << "empty!" << endl;
        stack<pNode> s;
        pNode cur = root;
        while (cur || !s.empty())
        {
            while (cur) //判斷:cur為空,遍歷棧頂結點的右子樹結點,cur不為空;訪問cur,並遍歷cur的左子樹結點
            {
                cout << cur->_data << "->";//遍歷一個結點則進行訪問
                s.push(cur);     
                cur = cur->_left;
            }
     
            cur = s.top(); //此時cur為空,因此需要對cur進行處理
            s.pop();    //將棧頂結點的右子樹結點壓入棧前,將棧頂結點彈出,避免死循環
            cur = cur->_right;//在下一個while時將右子樹結點壓入棧
        }
     
        cout << "over"<<endl;
    }

中序非遞歸:

中序時我們需要在遍歷完左子樹后訪問根節點,再去遍歷右子樹,因此我們僅需依照先序遍歷修改部分代碼。

    void inOrder_NR(pNode root)
    {
        if (NULL == root)
            cout << "empty" << endl;
        stack<pNode> s;
     
        pNode cur = root;
     
        while (cur || !s.empty())
        {
            while (cur)//壓入左子樹結點
            {
                s.push(cur);
                cur = cur->_left;
            }
     
            cur = s.top();//取棧頂結點
            cout << cur->_data << "->" ;//訪問左結點,此時棧頂節點有兩種情況:1.左子樹結點為空;2.左子樹結點已被訪問彈出
            s.pop();                   //彈出訪問過的結點
            cur = cur->_right;         //將棧頂結點的右子樹結點入棧
        }
        cout << "over" << endl;
    }

后序遍歷:

后序遍歷時由於訪問完左右子樹后才能訪問根結點,因此需要將根結點在棧內保留到左右子樹被訪問后,但同時會出現一個問題,當右子樹彈出后遇到根結點又會將右子樹結點壓入棧中,造成死循環,因此我們需要在定義一個變量last代表最后一個訪問的結點,當last與棧頂結點的右子樹結點相同時,則不再將右子樹結點壓入棧中。

    void pastOrder_NR(pNode root)
    {
        if (NULL == root)
            cout << "empty" << endl;
     
        pNode cur = root;
        pNode last = NULL;
        stack<pNode> s;
     
        while (cur || !s.empty())
        {
            while (cur)//壓入左子樹結點
            {
                s.push(cur);
                cur = cur->_left;
            }
     
            cur = s.top();
     
            if (cur->_right && last != cur->_right)//考慮棧頂結點的右子樹結點。存在且沒被訪問過,將右子樹結點壓入棧中
            {
                cur = cur->_right;
            }
            else if ((NULL == cur->_right) || (last == cur->_right))
                //右子樹結點為空或者已經被訪問過,則訪問棧頂結點並彈出
            {
                cout << cur->_data << "->";
                last = cur;//更新last值
                s.pop();
                //cur置空作用在於當原棧頂結點被訪問並彈出后,下一層while是將當前棧頂結點的左子樹入棧,當前棧頂結點的左子樹已經被遍歷過,        
                //因此會造成死循環,所以將cur置空,直接考慮當前棧頂結點的右子樹            
                //一旦某個結點入棧,首先會遍歷這個結點的左子樹,然后考慮右子樹的情況
                cur = NULL;
            }
        }cout << "over" << endl;
    }



附帶遞歸寫法用於測試正確性

    void prevOrder(pNode root)
    {
        if (root == NULL)
            return;
        cout << root->_data << "->";
        prevOrder(root->_left);
        prevOrder(root->_right);
    }
    void pastOrder(pNode root)
    {
        if (root == NULL) return;
        pastOrder(root->_left);
        pastOrder(root->_right);
        cout << root->_data << "->";
    }
     
    void inOrder(pNode root)
    {
        if (root == NULL) return;
        inOrder(root->_left);
        cout << root->_data << "->";
        inOrder(root->_right);
        
    }

    void test()
    {
        int arr[] = { 1, 2, 4, 10, '#', '#', 6, 11, '#', '#', 7,'#', '#', '#', 3, 8, '#', '#', 9 };
        size_t index = 0;
        pNode root=createTree_PrevOrder(arr, index, sizeof(arr)/sizeof(arr[0]), '#');
        
        cout << "prevOrder_NR:" ;
        prevOrder_NR(root);
        cout << "prevOrder   :";
        prevOrder(root);
        cout << endl;
        cout << "inOrder_NR:";
        inOrder_NR(root);
        cout << "inOrder   :";
        inOrder(root);
        cout << endl;
        cout << "pastOrder_NR:";
        pastOrder_NR(root);
        cout << "pastOrder   :";
        pastOrder(root);
        cout << endl;
    }



測試結果:


---------------------
版權聲明:本文為CSDN博主「Mr_Wayne__」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_38134165/article/details/79871430


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM