关于二叉树的结构与遍历

二叉树

二叉树是一棵特殊的树,二叉树每个节点最多有两个孩子结点,分别称为左孩子和右孩子。

满二叉树:高度为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;
            }
        }






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