• 数据结构-二叉树(7)树与森林


    一般树有4种常用表示方法:

    1.广义表表示法

    2.父指针表示法

    寻找父指针的操作时间复杂度为O(1),但寻找子女的操作时间复杂度达到O(n)

    3.子女链表表示法

    适合需要频繁寻找子女的应用

    寻找子女的操作在子女链表中进行,时间复杂度为O(d),d为树的度。但寻找父结点的操作时间复杂度达到O(n)

    4.子女-兄弟链表示法

    用二叉树表示一般树,每个结点的度都为2,由三个域组成:data、firstChild、nextSibling。

    若要访问x结点的第k个子女,只要从x->firstChild开始沿着nextSibling继续走k-1步。当nextsibling指针为空时为最后一个子女。

    适合需要频繁寻找子女的应用

    寻找子女的操作在子女链表中进行,时间复杂度为O(d),d为树的度。但寻找父结点的操作时间复杂度达到O(n)

    template <class T>
    struct TreeNode{
        T data;
        TreeNode<T> *firstChild,*nextSibling;
        TreeNode(T value=0,TreeNode<T> *fc=NULL,TreeNode<T> *ns=NULL):data(value),firstChild(fc),nextSibling(ns){};
    };
    
    template <class T>
    class Tree{
    private:
        TreeNode<T> *root,*current;  //如果不设*current指针,每次插入新结点都必须从头开始逐个结点比较
        bool Find(TreeNode<T> *p,T value);
        void RemovesubTree(TreeNode<T> *p);
        bool FindParent(TreeNode<T> *t,TreeNode<T> *p);
    public:
        Tree(){root=current=NULL;};
        bool Root();
        bool Isempty(){return root==NULL;}
        bool FirstChild();
        bool NextSibling();
        bool Parent();
        bool Find(T target);
    };
    
    template <class T>
    bool Tree<T>::Root(){
        //寻找根使之成为当前结点
        if(root==NULL){
            current=NULL;
            return false;
        }
        else{
            current=root;
            return true;
        }
    }
    
    template <class T>
    bool Tree<T>::Parent() {
        //在树中寻找当前结点current的父结点,使之成为当前结点
        TreeNode<T> *p=current;
        if(current==NULL||current==root){
            current=NULL;
            return false;
        }
        return FindParent(root,p);
    }
    
    template <class T>
    bool Tree<T>::FindParent(TreeNode<T> *t, TreeNode<T> *p) {
        //在根为*t的树中寻找*p的父结点,并使之成为当前结点current
        TreeNode<T> *q=t->firstChild;
        bool succ;
        while(q!=NULL && q!=p){   //没找到时,递归搜索子树
            if((succ=FindParent(q,p))==true) return succ; //子树中找到
            q=q->nextSibling;   //子树中没找到,搜寻下一个兄弟
        }
        if(q!=NULL && q==p){
            current=t;
            return true;
        }
        else{
            current=NULL;
            return false;
        }
    }
    
    template <class T>
    bool Tree<T>::FirstChild() {
        //在树中找当前结点的长子,并使之成为当前结点
        if(current==NULL||current->firstChild==NULL){
            current=NULL;
            return false;
        }
        current=current->firstChild;
        return true;
    }
    
    template  <class T>
    bool Tree<T>::NextSibling() {
        //在树中找当前结点的下一个,并使之成为当前结点
        if(current!=NULL&&current->nextSibling!=NULL){
            current=current->nextSibling;
            return true;
        }
        current==NULL;
        return false;
    }
    
    template <class T>
    bool Tree<T>::Find(T target) {
        if(Isempty()) return false;
        return Find(root,target);
    }
    
    template <class T>
    bool Tree<T>::Find(TreeNode<T> *p, T value) {
        //在根为*p的树中找值为value的结点,找到后该结点成为当前结点,否则当前结点不变
        bool result=false;
        if(p->data==value){
            result=true;
            current=p;
        }
        else{
            TreeNode<T> *q=p->firstChild;
            while(q!=NULL && !(result=Find(q,value)))
                q=q->nextSibling;
        }
        return result;
    }

    树的深度优先遍历通常包括先根次序遍历和后根次序遍历,不适合定义中序遍历,访问根结点操作的位置较难确定(子女个数不确定),因为子树没有顺序,只能人为定义

    #include <iostream.h>
    #include "tree.h"
    #include "queue.h"
    
    template <class T>
    void PreOrder(ostream& out,TreeNode<T> *p){
        //先根次序遍历并输出以*p为根的树:先访问树的根结点,再依次先根次序遍历树的每一棵子树
        if(p!=NULL){
            out<<p->data;
            for(p=p->firstChild;p!=NULL;p=p->nextSibling) PreOrder(out,p);
        }
    }
    
    template <class T>
    void PostOrder(ostream& out,TreeNode<T> *p){
        //后根次序遍历并输出以*p为根的树
        if(p!=NULL){
            TreeNode<T> *q;
            for(q=p->firstChild;q!=NULL;q=q->nextSibling)
                PostOrder(out,q);
            out<<p->data;
        }
    }
    
    template <class T>
    void LevelOrder(ostream& out,TreeNode<T> *p){
        //按广度优先次序(层次次序)分层遍历树,树的根结点是*p,算法中用到一个队列
        Queue<TreeNode<T> *> Q;
        if(p!=NULL){
            Q.EnQueue(p);
            while(!Q.IsEmpty()){
                Q.DeQueue(p);
                out<<p->data;
                for(p=p->firstChild;p!=NULL;p=p->nextSibling)
                    Q=EnQueue(p);
            }
        }
    }

    可以用这种表示法下的二叉树表示森林,此时第一棵树的根的子树森林转化成左子树,剩余其它树组成的森林转换成右子树:

    1)先根次序依次遍历森林里每一棵树:前序遍历二叉树即可

    template <class T>
    void preorder(TreeNode<T> *t,void (*visit)(TreeNode<T> *p)){
        if(t==NULL) return;
        visit(t);
        preorder(t->firstChild,visit);
        preorder(t->nextSibling,visit);
    }

    2)后根次序依次遍历森林里每一棵树:中序遍历二叉树即可

    template <class T>
    void postorder(TreeNode<T> *t,void (*visit)(TreeNode<T> *p)){
        if(t==NULL) return;
        postorder(t->firstChild,visit);
        visit(t);
        postorder(t->nextSibling,visit);
    }

    3)层次序遍历整个森林:先把所有树的根结点加入队列

    template <class T>
    void Levelorder(TreeNode<T> *t,void (*visit)(TreeNode<T> *p)){
        if(t==NULL) return;
        Queue<TreeNode<T>*> Q;
        for(;t!=NULL;t=t->nextSibling) Q.EnQueue(t);
        while(!Q.IsEmpty()){
            Q.DeQueue(t);
            visit(t);
            for(t=t->firstChild;t!=NULL;t=t->nextSibling) Q=EnQueue(t);
        }
    }
  • 相关阅读:
    浅谈面向对象语言中对象的使用
    淘宝店铺搜索工具(提升淘宝店铺排名人气)
    JavaScript学习总结二:js闭包(Closure)概念
    JavaScript学习总结一:js常见问题
    GC原理解析(c#)
    VS2010中的测试(2)——单元测试
    VS2010中的测试(3)——数据驱动单元测试
    领域驱动设计实践(二)
    俞敏洪在清华励志演讲
    Ioc最佳实践
  • 原文地址:https://www.cnblogs.com/yangyuliufeng/p/9448748.html
Copyright © 2020-2023  润新知