關於二叉樹的結構與遍歷

二叉樹

二叉樹是一棵特殊的樹,二叉樹每個節點最多有兩個孩子結點,分別稱爲左孩子和右孩子。

滿二叉樹:高度爲N的滿二叉樹有2^N - 1個節點的二叉樹。

完全二叉樹: 若設二叉樹的深度爲h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的結點都連續集中在最左邊,這就是完全二叉樹

遍歷方式:

前序遍歷(先根遍歷):(1):先訪問根節點;  (2):前序訪問左子樹;(3):前序訪問右子樹; 

中序遍歷:          (1):中序訪問左子樹;(2):訪問根節點;    (3):中序訪問右子樹;  

後序遍歷(後根遍歷):(1):後序訪問左子樹;(2):後序訪問右子樹;(3):訪問根節點;      

層序遍歷:         (1):一層層節點依次遍歷。   

程序實現

#include<iostream>
#include<queue>
 
using namespace std;
 
template<class T>
struct BinaryTreeNode//樹節點的結構體
{
    BinaryTreeNode<T>* _left;
    BinaryTreeNode<T>* _right;
    T _data;
 
    BinaryTreeNode(const T&x)
        :_left(NULL)
        , _right(NULL)
        , _data(x)
    {}
};
/*
實現如下接口
1.前序輸出:void PrevOrder();
2.中序輸出:void InOrder();
3.後序輸出:void PostOrder();
4.層序輸出:void Levelorder();
5.求結點個數:size_t Size();
6.求葉子結點:size_t LeafSize();
7.求深度(距離根結點最遠的路徑):size_t Depth();
*/
template<class T>
class BinaryTree
{
public:
    BinaryTree()//無參的構造函數
        :_root(NULL)
    {}
    BinaryTree(const T*a, size_t size, const T&invalid)//有參的構造函數
    {
        size_t index = 0;
        _root=_creatTree(a, size, index,invalid);
    }
    BinaryTree(const BinaryTree<T>&b)//拷貝構造
    {
        _root = _copy(b._root);
    }
    BinaryTree<T>operator=(const BinaryTree<T>&b)//賦值函數
    {
        if (this != &b)//檢測自賦值
        {
            BinaryTreeNode<T>*tmp = _copy(b._root);
            _Destroy(_root);
            _root = tmp;
        }
        return *this;
    }
    /*
    現代寫法
    BinaryTree<T>operator=( BinaryTree<T>&b)
    {
    if (this != &b)
    {
    swap(_root,b._root);
 
    }
    return *this;
    }
    */
    ~BinaryTree()//析構
    {
        _Destroy(_root);
    }
public:
    void PrevOrder()//前序
    {
        _printPrevOrder(_root);
        cout << endl;
    }
    void InOrder()//中序
    {
        _printInOrder(_root);
        cout << endl;
    }
    void PostOrder()//後序
    {
        _printPostOrder(_root);
        cout << endl;
    }
    void Levelorder()//層序
    {
        _printLevelorder(_root);
        cout << endl;
    }
    size_t Size()//求結點個數
    {
        return _size(_root);
    }
    size_t LeafSize()//求葉結點個數
    {
        return _leafSize(_root);
    }
    size_t Depth()//求深度
    {
        return _depth(_root);
    }
protected:
    BinaryTreeNode<T>*_creatTree(const T*a, size_t size, size_t&index, const T&invalid)
    {
        BinaryTreeNode<T>*root = NULL;
        if ((a[index] != invalid)&&index<size)
        {
            root = new BinaryTreeNode<T>(a[index]);
            root->_left = _creatTree(a, size, ++index, invalid);
            root->_right = _creatTree(a, size, ++index, invalid);
        }
        return root;
    }
     
    BinaryTreeNode<T> *_copy(BinaryTreeNode<T>*root)//賦值函數調用
    {
        BinaryTreeNode<T>*newroot = NULL;
        if (root == NULL)
            return NULL;
        newroot= new BinaryTreeNode<T>(root->_data);
        newroot->_left = _copy(root->_left);
        newroot->_right = _copy(root->_right);
             
        return newroot;
    }
     
    void _printPrevOrder(BinaryTreeNode<T>*root)//前序
    {
        if (root == NULL)
        {
            return;
        }
        cout << root->_data<<' ';
        _printPrevOrder(root->_left);
        _printPrevOrder(root->_right);
    }
     
    void _printInOrder(BinaryTreeNode<T>*root)//中序
    {
        if (root == NULL)
        {
            return;
        }
        _printInOrder(root->_left);
        cout << root->_data<<' ';
        _printInOrder(root->_right);
    }
     
    void _printPostOrder(BinaryTreeNode<T>*root)//後序
    {
        if (root == NULL)
        {
            return;
        }
        _printPostOrder(root->_left);
        _printPostOrder(root->_right);
        cout << root->_data<<' ';
    }
     
    void _printLevelorder(BinaryTreeNode<T>* root)//層序
    {
        if (root == NULL)
            return;
        queue<BinaryTreeNode<T>*> q;//利用隊來存放
        q.push(root);
         
        while (q.size())//while(!q.empty())
        {
            if (q.front()->_left)
            {
                q.push(q.front()->_left);
            }
            if (q.front()->_right)
            {
                q.push(q.front()->_right);
            }
            cout << q.front()->_data<<" ";
            q.pop();
        }
    }
     
    size_t _size(BinaryTreeNode<T>*root)//求結點個數
    {
        if (root == NULL)
            return 0;
        return _size(root->_left) + _size(root->_right) + 1;
    }
     
    size_t _leafSize(BinaryTreeNode<T>*root)//求葉子個數
    {
        if (root == NULL)
            return 0;
         
        if ((root->_left == NULL) && (root->_right == NULL))
        {
            return 1;
        }
        return _leafSize(root->_left) + _leafSize(root->_right);
    }
     
    size_t _depth(BinaryTreeNode<T>*root)//深度
    {
        int leftdepth = 0;
        int rightdepth = 0;
        if (root == NULL)
            return 0;
        else
        {
            leftdepth = _depth(root->_left);
            rightdepth = _depth(root->_right);
            /*if (leftdepth > rightdepth)
            {
                return leftdepth + 1;
            }
            else
                return rightdepth + 1;*/
            return leftdepth > rightdepth ? leftdepth + 1 : rightdepth + 1;
        }
    }
     
    void _Destroy(BinaryTreeNode<T>*root)
    {
        if (root == NULL)//空樹直接返回
            return;
        if ((root->_left == NULL) && (root->_right == NULL))//無左右孩子
            delete root;
        else
        {
            _Destroy(root->_left);
            _Destroy(root->_right);
        }
    }
private:
    BinaryTreeNode<T>* _root;
};
思考:

求樹節點個數上面的程序中是用遞歸的方式實現,若以遍歷樹的方式一個一個計算結點個數程序如下。

//size()實現方式2
size_t BinaryTree<T>::Size()//求結點個數
    {
        static size_t sSize = 0;
        _size(_root, sSize);
            return sSize;
    }
void BinaryTree<T>::_size(BinaryTreeNode<T>*root,size_t &sSize)
{
    if (root == NULL)
        return ;
    sSize++;
    _size(root->_left, sSize);
    _size(root->_right, sSize);
 
}

static修飾的變量有了靜態的屬性,在數據段,它的作用域與自動變量相同,但生存期延長,故可以以靜態變量引用的方式來傳參(全局變量也類似於靜態變量的情況)


ps: 棧用於存儲局部變量,堆用於存儲動態開闢,數據段存儲全局和靜態變量,代碼段存放常量和指令(只讀)。


    然而這兩種實現方式(使用全局變量和靜態變量)在測試時卻出現了很大的問題,如果測試函數中同時調用size函數,則在調用第二次的時候結點個數發生了錯誤,此時,涉及到了“線程安全”的問題。

    原因是數據段是線程公共的,而他們都調用了size函數size函數又引用了共有的數據段變量,故發生了錯誤。而局部變量不會發生這個問題,因爲局部變量存在棧中,是每個線程獨有的,故不會有線程安全的問題。

//size實現方式3
size_t BinaryTree<T>::Size()//求結點個數
    {
        size_t size = 0;
        _size(_root,size);
            return size;
    }
void BinaryTree<T>::_size(BinaryTreeNode<T>*root,size_t &size)
    {
        if (root == NULL)
            return ;
        size++;
        _size(root->_left,size);
        _size(root->_right,size);
 
    }
遍歷方式

1.前序的非遞歸實現

void _printPrevOrder(BinaryTreeNode<T>*root)//用棧來存放結點
    {
        if (root == NULL)
            return;
        stack<BinaryTreeNode<T>*> s;
        s.push(root);
         
        while (!s.empty())
        {
            BinaryTreeNode<T>*cur = s.top();
            s.pop();
             
            cout << cur->_data << " ";
            if (cur->_right != NULL)
                s.push(cur->_right);
            if (cur->_left != NULL)
                s.push(cur->_left);
        }
    }
2.中序的非遞歸實現

void _printInOrder(BinaryTreeNode<T>*root)
    {
        BinaryTreeNode<T>*cur = root;
        stack<BinaryTreeNode<T>*>s;
         
        while (cur || !s.empty())
        {
            while (cur)
            {
                s.push(cur);
                cur = cur->_left;
            }
         
            BinaryTreeNode<T>*top = s.top();
            cout << top->_data << " ";
            cur = top->_right;
            s.pop();
        }
    }

3.後序的非遞歸實現

void _printPostOrder(BinaryTreeNode<T>* root)
    {
        stack<BinaryTreeNode<T>*>s;
        BinaryTreeNode<T>*cur = root;
        BinaryTreeNode<T>*prev = NULL;
        while (cur!=NULL || !s.empty())
        {
            while (cur!=NULL)
            {
                s.push(cur);
                cur = cur->_left;
            }
            cur = s.top();
            if (cur->_right == NULL || cur->_right == prev)
            {
                cout << cur->_data << " ";
                prev = cur;
                s.pop();
                cur = NULL;
            }
            else
            {
                cur = cur->_right;
            }
        }






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章