关于二叉树的所有操作

  • 合并两棵子树成一棵大树
  • 判断一棵二叉树树是不是完全二叉树
  • 判断一棵二叉树是不是满二叉树
  • 计算一棵二叉树的高度
  • 计算一棵二叉树的节点数
  • 判断两棵二叉树是不是相同
  • 前序遍历(递归和非递归实现)
  • 中序遍历(递归和非递归实现)
  • 后续遍历(递归和非递归实现)
/*********************************
*
*   使用指向节点实现二叉树的节点表示
*   copyright @ : majin
*   time        : 2017 / 4 / 19
**********************************/
template<class T>
class BTreeNode{
public:
    BTreeNode(T da){
        data = da;
    }
    BTreeNode(T da, BTreeNode<T> *l, BTreeNode<T> *r){
        data = da;
        lChild = l;
        rChild = r;
    }

    T data;
    BTreeNode<T> *lChild;//指向左孩子
    BTreeNode<T> *rChild;//指向右孩子
};
/*********************************
*
*   使用数组实现二叉树
*   copyright @ : majin
*   time        : 2017 / 4 / 19
**********************************/
#include"BtreeNode.h"
#include<queue>
#include<stack>
template<class T>
int getNodeDeep(BTreeNode<T> *node);
template<class T>
bool isEqualHeight(BTreeNode<T> *node);
template<class T>
void copyNode(BTreeNode<T> *ynode, BTreeNode<T> *cnode);
template<class T>
class BTree{
public:
    BTree(){ root = 0; }
    bool makeTree(T value,BTree<T> &l,BTree<T> &r);//构造树 本节点值+左树根节点+右树根节点
    bool isFullTree();//判断一棵树是不是满二叉树
    bool isCompleteTree();//判断一棵树是不是完全二叉树
    int getHeight(){//计算高度
        return getNodeDeep(root);
    }
    bool isEmpty(){ return !root; }//看看树是不是空
    bool isSample(const BTree<T> &tree);//确定两棵二叉树是否一样
    bool deleteTree(); //删除整棵树
    bool calc();//数学表达式树,计算表达式  
    bool getCalc();//数学表达式树,给出对应带括号的表达式
    void preVisit(void(*visit)(BTreeNode<T>*node));//前序遍历
    void inVisit(void(*visit)(BTreeNode<T>*node));//中序遍历
    void postVisit(void(*visit)(BTreeNode<T>*node));//后续遍历
    void levelVisit(void(*visit)(BTreeNode<T>*node));//层序遍历
    void preNonDG(void(*visit)(BTreeNode<T>*node));//前序遍历的非递归
    void inNonDG(void(*visit)(BTreeNode<T>*node));//中序遍历的非递归
    void postNonDG(void(*visit)(BTreeNode<T>*node));//后序遍历的非递归
    BTreeNode<T> *root;
};

//构造树 本节点值+左树根节点+右树根节点
template<class T>
bool BTree<T>::makeTree(T value, BTree<T> &l, BTree<T> &r){
    try{
        root = new BTreeNode<T>(value, l.root, r.root);
        l.root = 0;
        r.root = 0;
        return true;
    }
    catch (...){
        return false;
    }
}
//得到一个节点的深度
template<class T>
int getNodeDeep(BTreeNode<T> *node){
    if (!node){
        return 0;
    }
    int ldeep = getNodeDeep(node->lChild);//得到左子树的深度
    int rdeep = getNodeDeep(node->rChild);//得到右子树的深度
    return ldeep > rdeep ? ldeep + 1 : rdeep + 1;
}

//判断一棵树是不是满二叉树
//一种方法(公式法):求出节点数n、二叉树的高度h,判断2^h-1=n
template <class T>
bool BTree<T>::isFullTree(){
    return  isEqualHeight(root);
}

//判断一个节点的两端是不是一样高
template<class T>
bool isEuqalHeight(BTreeNode<T> *node){
    if (!node){
        return true;
    }
    int ldeep = getNodeDeep(node->lChild);
    int rdeep = getNodeDeep(node->rChild);
    if (ldeep != rdeep){
        return false;
    }
    else{
        return isFull(node->lChild) && isFull(node->rChild);
    }
}

//判断一棵树是不是完全二叉树
template<class  T>
bool BTree<T>::isCompleteTree(){//判断一棵树是不是完全二叉树
    queue<BTreeNode<T>*> q;
    BTreeNode<T> *node;
    q.push(root);
    bool flag = false;
    while (!q.empty()){
        node = q.front();
        q.pop();
        if (node->lChild){
            if (!flag){ 
                q.push(node->lChild); 
            }
            else{
                return false;
            }
        }
        else{
            flag = true;
        }
        if (node->rChild){
            if (!flag){
                q.push(node->rChild);
            }
            else{
                return false;
            }
        }
        else{
            flag = true;
        }
    }
    return true;
}

//前序遍历节点
template<class T>
void preVisitNode(void(*visit)(BTreeNode<T>*node),BTreeNode<T> *node){
    if (node){
        visit(node);
        preVisitNode(visit,node->lChild);
        preVisitNode(visit,node->rChild);
    }
}

//前序遍历
template<class T>
void BTree<T>::preVisit(void(*visit)(BTreeNode<T>*node)){
    preVisitNode(visit,root);
}

//后序遍历节点
template<class T>
void postVisitNode(void(*visit)(BTreeNode<T>*node), BTreeNode<T> *node){
    if (node){
        postVisitNode(visit,node->lChild);
        postVisitNode(visit,node->rChild);
        visit(node);
    }
}
//中序遍历节点
template<class T>
void inVisitNode(void(*visit)(BTreeNode<T>*node), BTreeNode<T> *node){
    if (node){
        inVisitNode(visit,node->lChild);
        visit(node);
        inVisitNode(visit,node->rChild);
    }
}
template<class T>
void BTree<T>::inVisit(void(*visit)(BTreeNode<T>*node)){//中序遍历
    inVisitNode(visit,root);
}
template<class T>
void BTree<T>::postVisit(void(*visit)(BTreeNode<T>*node)){//后续遍历
    postVisitNode(visit,root);
}
template<class T>
void BTree<T>::levelVisit(void(*visit)(BTreeNode<T>*n)){//层序遍历
    queue<BTreeNode<T>*> q;
    q.push(root);
    BTreeNode<T> *node;
    while (!q.empty()){
        node = q.front();
        q.pop();
        visit(node);
        if (node->lChild){
            q.push(node->lChild);
        }
        if (node->rChild){
            q.push(node->rChild);
        }
    }
}

//判断两个节点是不是一样
template<class T>
bool isSampleNode(BTreeNode<T> *t1,BTreeNode<T> *t2){
    if (t1 && t2){
        //两个节点都不是空
        if (t1->data != t2->data){
            return false;
        }
        else{
            return isSampleNode(t1->lChild,t2->lChild) && isSampleNode(t1->rChild,t2->rChild);
        }

    }
    else if (!t1&&!t2){
        //两个节点都是空
        return true;
    }
    else{
        //一个空,一个不是空,那么这两棵树一定不同
        return false;
    }
}
template<class T>
bool BTree<T>::isSample(const BTree<T> &tree){//确定两棵二叉树是否一样
    return isSampleNode(root,tree.root);
}
template <class T>
void deleteNode(BTreeNode<T> *node){
    delete node;
}
template<class T>
bool BTree<T>::deleteTree(){ //删除整棵树
    try{
        postVisit(deleteNode);
        root = 0;
        return true;
    }
    catch(...){
        return false;
    }
}
/***********************************************
    * 各种非递归的遍历:遍历思想和我们手动遍历树是一样的,
    * 前序:先遍历当前节点,再遍历左子树,左子树遍历完了再遍历右子树。注意到:最后遍历完左子树的节点优先遍历右子树。
    * 中序:左-根-右。注意到:最后遍历完左子树的节点,优先被遍历,且节点被遍历完接着遍历右子树。
    * 后续:左-右-根。这里笔者想到的是最笨但最易理解的方法:当一个节点的左右子树都被遍历完了,我们再遍历这个节点。(使用了类似Java中的包装的设计思想)
    * copy right @ : majin
    * time :2017 / 4 / 20
    *********************************************/
//前序遍历的非递归
template<class T>
void BTree<T>::preNonDG(void(*visit)(BTreeNode<T>*node)){
    stack<BTreeNode<T>*> s;
    BTreeNode<T> *node = root;
    do{
        while (node){
            visit(node);
            s.push(node);
            node = node->lChild;
        }
        //找到栈顶的节点,这个最后访问完左子树的节点,右子树优先访问
        if (!s.empty()){
            node = s.top();
            s.pop();
            node = node->rChild;
        }
    } while (node||!s.empty());
}

//中序遍历的非递归
template<class T>
void BTree<T>::inNonDG(void(*visit)(BTreeNode<T>*node)){
    stack<BTreeNode<T>*> s;
    BTreeNode<T> *node = root;
    do{
        while (node){
            s.push(node);
            node = node->lChild;
        }
        //找到栈顶的节点,最后访问完左子树的节点优先被访问,且接着访问右子树
        if(!s.empty()){
            node = s.top();
            s.pop();
            visit(node);
            node = node->rChild;
        }
    } while (node || !s.empty());
}



//后序遍历的非递归
//根据后序遍历的特点:只有这个节点左孩子和右孩子都遍历完了,才会遍历这个节点,那么我们就记录下这个节点的左右孩子是不是被遍历过了
template<class T>
class BNodeLRVisited{
public:
    BNodeLRVisited(BTreeNode<T> *n){
        node = n; l = false; r = false;
    }
    BTreeNode<T> *node;
    bool l;//记录左边孩子是不是被访问了
    bool r;//同上
};
template<class T>
void BTree<T>::postNonDG(void(*visit)(BTreeNode<T>*node)){
    stack<BNodeLRVisited<T>*> s;
    BNodeLRVisited<T> *lrnode = new BNodeLRVisited<T>(root);
    s.push(lrnode);
    do{
        if (lrnode && lrnode->l && lrnode->r){//lrnode节点的左右节点都被访问过,那么就要访问他了
            visit(lrnode->node);
            s.pop();
        }
        while (lrnode && !lrnode->l){//若左孩子没访问过,则访问;若访问过,则不需访问左孩子
            lrnode->l = true;//改变lrnode的l标记

            if (lrnode->node->lChild){//左孩子不空,左孩子入栈
                lrnode = new BNodeLRVisited<T>(lrnode->node->lChild);//改变lrnode为左孩子(下面处理左孩子)
                s.push(lrnode);
            }
            else{
                lrnode = 0;//左孩子为空,退出遍历(break掉也行)
            }
        }

        //找到栈顶的节点
        if (!s.empty()){
            lrnode = s.top();
            if ( lrnode->node->rChild && !lrnode->r){//右节点存在且右节点没访问
                lrnode->r = true;//改变r标记
                lrnode = new BNodeLRVisited<T>(lrnode->node->rChild);//改变lrnode为右孩子(下面处理右孩子)
                s.push(lrnode);
            }else{//右孩子为空或右孩子被访问过
                lrnode->r = true;//只需要改变r标记,不存在右节点进栈操作
            }
        }
    } while (!s.empty());//栈中还有未访问的节点,继续
};

其他的实现方法

template<class T>
class BTree{
...
...
...
/**********************************************
    * 上网查找
    * copyright@:majin
    * time:2017 / 4 / 20
    ************************************************/
    //前序二:每次出栈一个,输出;右孩子进栈,左孩子进栈(注意顺序)
    void preNonDG2(void(*visit)(BTreeNode<T>*node));//
    //后序二:双指针,一个指向当前访问的节点,一个指向上一个访问的节点
    //若上一个访问的节点是当前访问节点的右孩子。那么当前节点访问
    void postNonDG2(void(*visit)(BTreeNode<T>*node));
    //后序三:双栈法
    void postNonDG3(void(*visit)(BTreeNode<T>*node));
...
...
}
//双指针
template<class T>
void BTree<T>::postNonDG2(void(*visit)(BTreeNode<T>*node)){
    BTreeNode<T> *curr = root;;//当前要访问的节点
    BTreeNode<T> *pre=0;//上一个访问的节点

    stack<BTreeNode<T>*> s;

    while (curr || !s.empty()){
        while (curr){
            s.push(curr);
            curr = curr->lChild;
        }
        curr = s.top();
        if (!curr->rChild || curr->rChild == pre){//当前节点没有右孩子或右孩子被访问过了,访问当前节点
            visit(curr);
            s.pop();
            pre = curr;
            curr = 0;
        }
        else{//当前节点有右孩子,访问右孩子
            curr = curr->rChild;
        }
    }


}

//双栈
template<class T>
void BTree<T>::postNonDG3(void(*visit)(BTreeNode<T>*node)){
    stack<BTreeNode<T>*> s1, s2;

    BTreeNode<T>* curr = 0;//当前要访问的节点
    s1.push(root);
    while ( !s1.empty()){
        curr = s1.top();
        s1.pop();
        s2.push(curr);
        if (curr->lChild){
            s1.push(curr->lChild);
        }
        if (curr->rChild){
            s1.push(curr->rChild);
        }
    }
    while (!s2.empty()){
        curr = s2.top();
        s2.pop();
        visit(curr);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值