1. 程式人生 > >二叉樹的遞迴和非遞迴遍歷

二叉樹的遞迴和非遞迴遍歷

二叉樹的遍歷

  • 前序遍歷:
    遍歷順序:根節點,左子樹,右子樹
    遞迴遍歷:訪問根節點,遞迴呼叫再訪問左子樹,右子樹
//前序遍歷
    void PrevOrder()
    {
        _PrevOrder(_root);
        cout << endl;
    }
    void _PrevOrder(Node* root)
    {
        if (root == NULL)
        {
            return;
        }
        cout << root->_data <<
" ";//根節點 _PrevOrder(root->_leftNode);//左子樹 _PrevOrder(root->_rightNode);//右子樹 }

非遞迴遍歷:壓入根節點的左子樹節點並訪問,取棧頂元素,對其右子樹節點進行壓棧(右子樹節點作為子樹的根節點說不定會有左子樹節點)。

這裡寫圖片描述

//前序遍歷
    void PrevOrder_NonR()
    {
        Node* cur = _root;
        stack<Node*> s;
        while (cur || !s.empty())
        {
            while
(cur) { cout << cur->_data << " "; s.push(cur); cur = cur->_leftNode; } Node* top = s.top(); cur = top->_rightNode; s.pop(); } cout << endl; }
  • 中序遍歷

    遍歷順序:左子樹,根節點,右子樹
    遞迴遍歷:遞迴呼叫左子樹並訪問,根節點,再右子樹
//中序遍歷
    void InOrder()
    {
        _InOrder(_root);
        cout << endl;
    }
    void _InOrder(Node* root)
    {
        if (root == NULL)
        {
            return;
        }
        _InOrder(root->_leftNode);//左子樹
        cout << root->_data << " ";//根節點
        _InOrder(root->_rightNode);//右子樹
    }

非遞迴遍歷:
和前序遍歷一樣,建立棧將根節點的左子樹節點壓入,但不訪問。在取棧頂元素的時候對節點進行訪問。

//中序遍歷
    void InOrder_NonR()
    {
        Node* cur = _root;
        stack<Node*> s;
        while (cur || !s.empty())
        {
            //壓入根節點的左子樹節點
            while (cur)
            {
                s.push(cur);
                cur = cur->_leftNode;
            }
            //取棧頂元素並進行訪問
            Node* top = s.top();
            cout << top->_data << " ";
            s.pop();
            //對右子樹節點進行遍歷
            cur = top->_rightNode;
        }
        cout << endl;
    }
  • 後序遍歷
    遍歷順序:左子樹,右子樹,根節點
    遞迴遍歷:
//後序遍歷
    void PostOrder()
    {
        _PostOrder(_root);
        cout << endl;
    }
    void _PostOrder(Node* root)
    {
        if (root == NULL)
        {
            return;
        }
        _PostOrder(root->_leftNode);//左子樹
        _PostOrder(root->_rightNode);//右子樹
        cout << root->_data << " ";//根節點
    }

非遞迴遍歷:
左子樹節點遍歷後不能對直接取棧頂元素進行pop操作,需要對其右子樹節點進行判斷,這裡需要記錄上次訪問的節點prevNode,右子樹節點為空或右子樹節點為上次訪問過的節點,都說明此子樹已經遍歷完成。

這裡寫圖片描述

//後序遍歷
    void PostOrder_NonR()
    {
        Node* cur = _root;
        Node* prevNode = NULL;//記錄上次訪問的節點
        stack<Node*> s;
        while (cur || !s.empty())
        {
            while (cur)
            {
                s.push(cur);
                cur = cur->_leftNode;
            }
            Node* top = s.top();
            if (top->_rightNode == NULL || top->_rightNode == prevNode)
            {
                cout << top->_data << " ";
                s.pop();
                prevNode = top;
            }
            else
            {
                cur = top->_rightNode;
            }
        }
        cout << endl;
    }

  • 層序遍歷
  1. 利用佇列先進先出的特點,將根節點壓進佇列,如果此根節點存在左子樹節點和右子樹節點,就將根節點先出佇列,再將左子樹節點和右子樹節點進佇列;
  2. 此次進入的左右子樹節點也會作為根節點有其對應的左右子樹節點,再進行根節點出佇列,左右子樹節點進佇列操作即可,直至所有節點全被遍歷。
//層序遍歷
    void LevelOrder()
    {
        queue<Node*> q;
        if (_root)
        {
            q.push(_root);
        }
        while (!q.empty())
        {
            Node* front = q.front();
            cout << front->_data << " ";
            q.pop();
            if (front->_leftNode)
            {
                q.push(front->_leftNode);
            }
            if (front->_rightNode)
            {
                q.push(front->_rightNode);
            }
        }
        cout << endl;
    }