DTLib - 通用树结构的设计与实现(Tree、TreeNode、GTree、GTreeNode)

目录

1、树的定义与操作

1、树的定义

2、树与结点抽象类的创建

2、通用树的存储结构与实现

1、通用树中结点的查找操作

2、通用树中结点的插入操作

3、通用树中结点的清除操作(工厂模式)

4、树中结点的删除操作

5、通用树中属性操作的实现

1、树中结点的数目

2、树的高度

3、树的度数

4、编程实验

3、树形结构的层次遍历

4、小结 


1、树的定义与操作

1、树的定义

树是一种非线性的数据结构 

树是由n (n≧0)个结点组成的有限集合 

      -如果n = 0 , 称为空树; 

      -如果n > 0 , 则: 

            ● 有—个特定的结点称之为根(root)的结点 

            ● 根结点只有直接后继,但没有直接前驱 

            ● 除根以外的其它结点划分为m (m ≧ 0)个互不相交的有限集合T0 , T1, …,Tm-1每个集合又是一棵树,

                并且称之为根的子树(sub tree) 

树的定义是递归的,n = 0是出口,所以与树相关的算法都是递归的

树的示例 

            

树中度的概念

      -树的结点包含一个数据及若干指向子树的分支 

      -结点拥有的子树数目称为结点的度 

            ● 度为0的结点称为叶结点 

            ● 度不为0的结点称为分支结点

      -树的度定义为所有结点中度的最大值 ,下面是度为3的树

            

树中的前驱后继

      -结点的直接后继称为该结点的孩子,相应的,该结点称为孩子的双亲

      -结点的孩子的孩子的......称为该结点的子孙 ,相应的,该结点称为子孙的祖先 

      -同一个双亲的孩子之间互称兄弟

                

 树中结点的层次 

      -根为第1层 

      -根的孩子为第2层

      -...

            

                                            树中结点时最大层次称为树的深度或高度

树的有序性

      -如果树中结点的各子树从左向右是有次序的,子树间不能互换位置,则称该树为有序树,否则为无序树。 

 

    

森林的概念 

      -森林是由(n ≧ 0 )棵互不相交的树组成的集合

            

                                                    由3棵树组成的森林

2、树与结点抽象类的创建

树的一些常用操作 

      -将元素插入树中 

      -将元素从树中删除 

      -获取树的结点数 

      -获取树的高度 

      -获取树的度 

      -清空树中的元素

      -。。。

树与结点的类关系 

                    

编程实验 

树与结点抽象类的创建     Tree.h 

TreeNode.h

#ifndef TREENODE_H
#define TREENODE_H

#include "Object.h"

namespace DTLib
{

template < typename T >
class TreeNode : public Object
{

public:
    T value;
    TreeNode<T>* parent; // 指向父节点指针

    TreeNode()
    {
        parent = NULL;
    }

    virtual ~TreeNode() = 0;
};

template < typename T >
TreeNode<T>::~TreeNode()
{

}


}

#endif // TREENODE_H

Tree.h

#ifndef TREE_H
#define TREE_H

#include "TreeNode.h"
#include "SharedPointer.h"

namespace DTLib
{

template < typename T >
class Tree : public Object
{
protected:
    TreeNode<T>* m_root;


    Tree(const Tree<T>&);     // 树结构对象是不能够被复制的
    Tree<T>& operator = (const Tree<T>&);
public:
    Tree() { m_root = NULL; }
    virtual bool insert(TreeNode<T>* node) = 0;
    virtual bool insert(const T& value,TreeNode<T>* parent) = 0;
    virtual SharedPointer< Tree<T> > remove(const T& value) = 0;
    virtual SharedPointer< Tree<T> > remove(TreeNode<T>* node) = 0;
    virtual TreeNode<T>* find(const T& value) const = 0;
    virtual TreeNode<T>* find(TreeNode<T>* node) const = 0;
    virtual TreeNode<T>* root() const = 0;
    virtual int degree() const = 0;
    virtual int count() const = 0;
    virtual int hight() const = 0;
    virtual void clear() = 0;
};
}


#endif // TREE_H

main.cpp

#include <iostream>
#include"Tree.h"

using namespace std;
using namespace DTLib;


int main()
{
    Tree<int>* t;
    TreeNode<int>* tn;
    
    return 0;
}

2、通用树的存储结构与实现

目标 :完成结点的存储结构设计 

        

设计要点 

      -GTree通用树结构每个结点可以存在多个后继结点 

      -GTreeNode能够包含任意多指向后继结点的指针 

      -实现树结构的所有操作(增,删,查,等)

GTreeNode的设计与实现 

            

template < typename T >
class GTreeNode : public TreeNode<T>
{
public:
    LinkList< GTreeNode<T>* > child; // 包含任意多指向后继结点的指针 
};

GTree的设计与实现 

                     

GTree (通用树结构)的实现架构

                

                               可以看到B、C、D都包含指向父结点的指针

编程实验 

通用树结构的创建     GTree.h

GTreeNode.h

#ifndef GTREENODE_H
#define GTREENODE_H

#include "TreeNode.h"
#include "LinkList.h"

namespace DTLib
{

template < typename T >
class GTreeNode : public TreeNode<T>
{
public:
    LinkList< GTreeNode<T>* > child; // 包含任意多指向后继结点的指针 

};
}
#endif // GTREENODE_H

GTree.h

#ifndef GTREE_H
#define GTREE_H

#include "Tree.h"
#include "GTreeNode.h"

namespace DTLib
{
template < typename T >
class GTree : public Tree<T>
{
public:
    bool insert(TreeNode<T>* node)
    {
        bool ret = true; // 这里先写框架,后续完善

        return ret;
    }

    bool insert(const T& value, TreeNode<T>* parent)
    {
        bool ret = true;

        return ret;
    }

    SharedPointer< Tree<T> > remove(const T& value)
    {

        return NULL;
    }

    SharedPointer< Tree<T> > remove(TreeNode<T>* node)
    {

        return NULL;
    }

    GTreeNode<T>* find(const T& value) const   // 赋值兼容
    {
        return NULL;
    }
    GTreeNode<T>* find(TreeNode<T>* node) const
    {
        return NULL;
    }
    GTreeNode<T>* root() const
    {
        return dynamic_cast< GTreeNode<T>* >(this->m_root);
    }
    int degree() const
    {
        return 0;
    }
    int count() const
    {
        return 0;
    }
    int height() const
    {
        return 0;
    }
    void clear()
    {
        this->m_root = NULL;
    }

    ~GTree()
    {
        clear();
    }
};

}
#endif // GTREE_H

main.cpp 

#include <iostream>
#include "GTree.h"

using namespace std;
using namespace DTLib;


int main()
{
    GTree<int> t;
    GTreeNode<int> tn;

    return 0;
}
 

每个树结点中为什么包含指向前驱结点的指针? 

根结点 → 叶结点:非线性数据结构 

叶结点 → 根节点:线性数据结构(链表)

    

 

1、通用树中结点的查找操作

查找的方式 

   -基于数据元素值的查找 

             GTreeNode<T>*  find(const T& value) const 

   -基于结点的查找 

             GTreeNode<T>*  find(TreeNode<T>* node) const 

树中数据元素和结点的查找 

                

                                                                递归的思想

基于数据元素值的查找 

   -定义功能:find(node, value) 

              功能:在node为根结点的树中查找value所在的结点 

    

基于结点的查找 

   -定义功能:find(node, obj) 

              功能:在node为根结点的树中查找是否存在obj结点

                            

编程实验 

基于数据元素值的查找 ,基于结点的查找 

#ifndef GTREE_H
#define GTREE_H

#include "Tree.h"
#include "GTreeNode.h"

namespace DTLib
{
template < typename T >
class GTree : public Tree<T>
{
protected:
    GTreeNode<T>* find(GTreeNode<T>* node, const T& value) const
    {
        GTreeNode<T>* ret = NULL;

        if(node != NULL)
        {
            if(node->value == value)
            {
                return node;
            }
            else
            {
                // 遍历当前结点的每一棵子树
                for(node->child.move(0); !node->child.end() && (ret == NULL); node->child.next()) 
                {
                    ret = find(node->child.current(), value); // chlid里每一项都是指向子树(后继结点)的指针
                }
            }
        }

        return ret;
    }

    GTreeNode<T>* find(GTreeNode<T>* node, GTreeNode<T>* obj) const
    {
        GTreeNode<T>* ret = NULL;

        if(node == obj)
        {
            return node;
        }
        else
        {
            if(node != NULL)
            {
                for(node->child.move(0); !node->child.end() && (ret == NULL); node->child.next())
                {
                    ret = find(node->child.current(), obj);
                }
            }
        }

        return ret;
    }

public:
    // ...

    GTreeNode<T>* find(const T& value) const   
    {
        return find(root(), value);
    }
    GTreeNode<T>* find(TreeNode<T>* node) const
    {
        return find(root(), dynamic_cast< GTreeNode<T>* >(node));
    }

    GTreeNode<T>* root() const
    {
        return dynamic_cast< GTreeNode<T>* >(this->m_root);
    }
};

}
#endif // GTREE_H

main.cpp

#include <iostream>
#include "GTree.h"

using namespace std;
using namespace DTLib;


int main()
{
    GTree<int> t;
    GTreeNode<int> tn;

    t.find(0);

    t.find(&tn);

    return 0;
}

2、通用树中结点的插入操作

插入的方式 

   -插入新结点 

             bool insert(TreeNode<T>* node

   -插入数据元素 

             bool insert(const T& value, TreeNode<T>* parent)

如何指定新结点在树中的位置? 

问题分析 

       -树是非线性的,无法采用下标的形式定位数据元素 

       -每一个树结点都有唯一的前驱结点(父结点) 

       -因此,必须先找到前驱结点,才能完成新结点的插入 

新结点的插入 

            

插入新结点 

    

插入数据元素 

                    

编程实验 

树的插入操作     insert

bool insert(TreeNode<T>* node)  
   {  
       bool ret = true;  
  
       if(node != NULL)  
       {  
           if(this->m_root == NULL)  // 判断当前树是否一棵空树
           {  
               node->parent = NULL;  
               this->m_root = node;  
           }  
           else  
           {  
               GTreeNode<T>* np = find(node->parent);  // 查找指定的父结点是否存在于树中
  
               if( np != NULL )  
               {  
                   GTreeNode<T>* n = dynamic_cast<GTreeNode<T>*>(node);  
  
                   if( np->child.find(n) < 0 ) // 新结点是否已经存在于树中,只需要判断父节点的子结点链表里是否有新结点node
                   {  
                       np->child.insert(n);    // 新结点node插入到父结点的子结点链表里
                   }  
               }  
               else  
               {  
                   THROW_EXCEPTION(InvalidOperationException, "Invalid parent tree node ...");  
               }  
           }  
       }  
       else  
       {  
           THROW_EXCEPTION(InvalidParameterException, "Parameter node cannot be NULL ...");  
       }  
  
       return ret;  
   }  
  
   bool insert(const T& value, TreeNode<T>* parent)  
   {  
       bool ret = true;  
  
       GTreeNode<T>* node = new GTreeNode<T>();  
  
       if( node )  
       {  
           node->value = value;  
           node->parent = parent;  
  
           insert(node);  
       }  
       else  
       {  
           THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new tree node ...");  
       }  
  
       return ret;  
   }  

main.cpp

#include <iostream>
#include "GTree.h"

using namespace std;
using namespace DTLib;

int main()
{
    GTree<char> t;
    GTreeNode<char>* node = NULL;

    t.insert('A', NULL); // 作为根结点

    node = t.find('A');
    t.insert('B', node);
    t.insert('C', node);
    t.insert('D', node);

    node = t.find('B');
    t.insert('E', node);
    t.insert('F', node);

    node = t.find('E');
    t.insert('K', node);
    t.insert('L', node);

    node = t.find('C');
    t.insert('G', node);

    node = t.find('D');
    t.insert('H', node);
    t.insert('I', node);
    t.insert('J', node);

    node = t.find('H');
    t.insert('M', node);

    const char* s = "KLFGMIJ";   // 叶结点字符

    for(int i=0; i<7; i++)
    {
        TreeNode<char>* node = t.find(s[i]); // 找到叶结点

        while(node != NULL) // 遍历链表
        {
            cout << node->value << " "; 

            node = node->parent; 
        }

        cout << endl;
    }


    return 0;
}

 

                        

 

3、通用树中结点的清除操作(工厂模式)

清除操作的定义 

       -void clear() :将树中的所有结点清除(释放堆中的结点) 

树中数据元素的清除 

        

                                                    递归的思想(先清除子树再清除根结点)

清除操作功能的定义 

       -free( node ): 清除node为根结点的树,释放树中的每一个结点 

         

编程实验 

清除树中的结点

    void free(GTreeNode<T>* node)
    {
        if(node != NULL)
        {
            for(node->child.move(0); !node->child.end(); node->child.next())
            {
                free(node->child.current());  // 将根结点所有孩子清除 
            }
            
            delete node;    // 清除根结点
        }
    }
    void clear()
    {
        free(root());
        this->m_root = NULL;
    }

树中的结点可能来源于不同的存储空间,如何判断堆空间中的结点并释放? 

问题分析 

     -单凭内存地址很难准确判断具体的存储区域 

     -只有堆空间的内存需要主动释放(delete) 

     -清除操作时只需要对堆中的结点进行释放 

解决方案工厂模式 

       1. 在GTreeNode中增加保护成员变量m_flag 

       2. 将GTreeNode中的 operator new 重载为保护成员函数 

       3. 提供工厂方法GTreeNode<T>* NewNode() 

       4. 在工厂方法中new新结点并将m_flag设置为true (new新结点时给新结点打标记,表明为堆空间对象)

树结点的工厂模式示例

                

编程实验 

解决方案     Factory

GTreeNode.h

#ifndef GTREENODE_H
#define GTREENODE_H

#include "TreeNode.h"
#include "LinkList.h"

namespace DTLib
{

template < typename T >
class GTreeNode : public TreeNode<T>
{
protected:
    bool m_flag;
    
    bool operator new (unsigned int size) throw()   // 不能直接用new创建对象,而是提供静态方法
    {
        return Object::operator new(size);
    }

public:
    LinkList< GTreeNode<T>* > child;
    
    GTreeNode<T>()
    {
        m_flag = false;
    }
    
    bool flag()
    {
        return m_flag;
    }

    static GTreeNode<T>* NewNode()
    {
        GTreeNode<T>* ret = new GTreeNdoe<T>();
        
        if(ret != NULL)
        {
            ret->m_flag = true;       // 堆空间创建的对象标记为true,其他false
        }
        
        return ret;
    }
    

};
}
#endif // GTREENODE_H

GTree.h

    // ...

    void free(GTreeNode<T>* node)
    {
        if(node != NULL)
        {
            for(node->child.move(0); !node->child.end(); node->child.next())
            {
                free(node->child.current());
            }

            if( node->flag() )
            {
                delete node;    // 只有堆空间的对象才delete
            }
            else    
            {
                std::cout << node->value;
            }

        }
    }

public:
    // ...

    bool insert(const T& value,TreeNode<T>* parent)
    {
        bool ret = true;

        GTreeNode<T>* node = GTreeNode<T>::NewNode();  // 使用工厂方法创建对象

        if( node )
        {
            node->value = value;
            node->parent = parent;

            insert(node);
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new tree node ...");
        }

        return ret;
    }

    void clear()
    {
        free(root());
        this->m_root = NULL;
    }

    ~GTree()
    {
        clear();
    }

main.cpp

#include <iostream>
#include "GTree.h"

using namespace std;
using namespace DTLib;


int main()
{
    GTree<char> t;
    GTreeNode<char>* node = NULL;

    GTreeNode<char> root;

    root.value = 'A';
    root.parent = NULL;

    t.insert(&root);    // 将局部对象作为根结点插入

    node = t.find('A');
    t.insert('B', node);
    t.insert('C', node);
    t.insert('D', node);

    node = t.find('B');
    t.insert('E', node);
    t.insert('F', node);

    node = t.find('E');
    t.insert('K', node);
    t.insert('L', node);

    node = t.find('C');
    t.insert('G', node);

    node = t.find('D');
    t.insert('H', node);
    t.insert('I', node);
    t.insert('J', node);

    node = t.find('H');
    t.insert('M', node);

    t.clear();

    const char* s = "KLFGMIJ";

    for(int i=0; i<7; i++)
    {
        TreeNode<char>* node = t.find(s[i]);

        while(node != NULL)
        {
            cout << node->value << " ";

            node = node->parent;
        }

        cout << endl;
    }


    return 0;
}

 

                    

 

4、树中结点的删除操作

删除的方式 

     -基于数据元素值的删除 

                    SharedPointer< Tree<T> > remove(const T& value) 

     -基于结点的删除 

                    SharedPointer< Tree<T> > remove(TreeNode<T>* node) 

删除操作成员函数的设计要点 

     -将被删结点所代表的子树进行删除 

     -删除函数返回一棵堆空间中的树 

     -具体返回值为指向树的智能指针对象 

树中结点的删除

            

                                         返回智能指针对象就能对待删除子树不用时进行自动释放堆空间

实用的设计原则 

            当需要从函数中返回堆中的对象时,使用智能指针(SharedPointer)作为函数的返回值

下面举个例子帮助理解    

int* func()  
{  
    return new int;  
}  
  
int main()  
{  
    int* p = func();    //那么在main函数里要不要释放p所指堆空间???   
      
      
    return 0;  
} 

站在main函数角度,不应该释放,毕竟我们曾在C/C++学过谁申请应该谁释放

然而在这里func函数提供四个字节就返回了,哪有机会释放

那么我们可以用智能指针解决该问题

SharedPointer<int> func()  
{  
    return new int;  
}  
  
int main()  
{  
    SharedPointer<int> p = func(); // 在main函数结束时自动释放堆空间   
      
      
    return 0;  
}  

 删除操作功能的定义 

     -void remove(GTreeNode<T>* node,  GTree<T>*&  ret)     --> 返回一个引用作为指针别名

                ● 将node为根结点的子树从原来的树中删除 

                ● ret作为子树返回 ( ret指向堆空间中的树对象 )

删除功能函数的实现 

                    

编程实验

树结点的删除操作     remove

#ifndef GTREE_H
#define GTREE_H

#include "Tree.h"
#include "GTreeNode.h"
#include "Exception.h"


namespace DTLib
{
template < typename T >
class GTree : public Tree<T>
{
protected:
    // ...

    void remove(GTreeNode<T>* node, GTree<T>*& ret)
    {
        ret = new GTree<T>();

        if(ret == NULL)
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new tree ...");
        }
        else
        {
            if( root() == node)    // 待删结点就是根结点
            {
                this->m_root = NULL;
            }
            else
            {
                LinkList< GTreeNode<T>* >& child = dynamic_cast< GTreeNode<T>* >(node->parent)->child; // child引用为node父结点的child链表别名

                child.remove(child.find(node)); // 在父结点的child链表中 将node删除

                node->parent = NULL;    // 此子树彻底地离开了树,并没有清除释放
            }

            ret->m_root = node;
        }
    }

public:
    // ...

    SharedPointer< Tree<T> > remove(const T& value)
    {
        GTree<T>* ret = NULL;

        GTreeNode<T>* node = find(value); // 要删除的结点在当前树中

        if(node == NULL)
        {
            THROW_EXCEPTION(InvalidParameterException, "Can not find the node via parameter value ...");
        }
        else
        {
            remove(node, ret);
        }


        return ret;
    }
    SharedPointer< Tree<T> > remove(TreeNode<T>* node)
    {
        GTree<T>* ret = NULL;

        node = find(node);

        if(node == NULL)
        {
            THROW_EXCEPTION(InvalidParameterException, "Parameter node is invalid ...");
        }
        else
        {
            remove(dynamic_cast<GTreeNode<T>*>(node), ret);
        }

        return ret;
    }
};

}
#endif // GTREE_H

main.cpp

#include <iostream>
#include "GTree.h"

using namespace std;
using namespace DTLib;


int main()
{
    GTree<char> t;
    GTreeNode<char>* node = NULL;

    GTreeNode<char> root;

    root.value = 'A';
    root.parent = NULL;

    t.insert(&root);    // 将局部对象作为根结点插入

    node = t.find('A');
    t.insert('B', node);
    t.insert('C', node);
    t.insert('D', node);

    node = t.find('B');
    t.insert('E', node);
    t.insert('F', node);

    node = t.find('E');
    t.insert('K', node);
    t.insert('L', node);

    node = t.find('C');
    t.insert('G', node);

    node = t.find('D');
    t.insert('H', node);
    t.insert('I', node);
    t.insert('J', node);

    node = t.find('H');
    t.insert('M', node);

    const char* s = "KLFGMIJ";

    t.remove(t.find('B'));


    for(int i=0; i<7; i++)
    {
        TreeNode<char>* node = t.find(s[i]);

        while(node != NULL)
        {
            cout<<node->value<<" ";

            node = node->parent;
        }

        cout<<endl;
    }

    SharedPointer< Tree<char> > p = t.remove('D');

    for(int i=0; i<7; i++)
    {
        TreeNode<char>* node = p->find(s[i]);

        while(node != NULL)
        {
            cout << node->value << " ";

            node = node->parent;
        }

        cout << endl;
    }


    return 0;

 

                        

5、通用树中属性操作的实现

1、树中结点的数目

         定义功能: count(node):在node为根结点的树中统计结点数目

        树结点数目的计算示例:count(A) = count(B) + count(C) + count(D) +1

        

以A为根结点的树的结点数 = 以B为根结点的树的结点数 + 以C为根结点的树的结点数 + 以D为根结点的树的结点数 + 1

 

2、树的高度

定义功能: height(node):获取node为根结点的树的高度

        

树的高度计算示例:height(A) = MAX{ height(B), height(C), height(D) } + 1

    

3、树的度数

定义功能: degree( node ):获取node为根结点的树的度数

        

树的度计算示例:degree(a) = MAX{ degree(B), degree(C), degree(D), 3 }

                                          子树的度数与根结点的度数比较取最大值

4、编程实验

#ifndef GTREE_H
#define GTREE_H

#include "Tree.h"
#include "GTreeNode.h"
#include "Exception.h"

namespace DTLib
{
template < typename T >
class GTree : public Tree<T>
{
protected:
    // ...

    int count(GTreeNode<T>* node) const
    {
        int ret = 0;

        if(node != NULL) 
        {
            ret = 1;    

            for(node->child.move(0); !node->child.end(); node->child.next())
            {
                ret += count(node->child.current()); // count(A) = count(B) + count(C) + count(D) +1
            }
        }

        return ret;  
    }

    int height(GTreeNode<T>* node) const
    {
        int ret = 0;

        if(node != NULL)
        {
            for(node->child.move(0); !node->child.end(); node->child.next())
            {
                int h = height(node->child.current()); 

                if(ret < h)
                {
                    ret = h;
                }
            }

            ret = ret + 1;  // 子树最大高度 + 1
        }


        return ret;
    }

    int degree(GTreeNode<T>* node) const
    {
        int ret = 0;

        if(node != NULL)
        {
            ret = node->child.length(); // 根结点的度

            for(node->child.move(0); !node->child.end(); node->child.next())
            {
                int d = degree(node->child.current());  // 每个孩子的度

                if(ret < d)     
                {
                    ret = d;  // degree(a) = MAX{ degree(B), degree(C), degree(D), 3 }
                }
            }
        }

        return ret;
    }

public:
    // ...

    int degree() const
    {
        return degree(root());
    }
    int count() const
    {
        return count(root());
    }
    int height() const
    {
        return height(root());
    }
};

}
#endif // GTREE_H

main.cpp

#include <iostream>
#include "GTree.h"

using namespace std;
using namespace DTLib;


int main()
{
    GTree<char> t;
    GTreeNode<char>* node = NULL;

    GTreeNode<char> root;

    root.value = 'A';
    root.parent = NULL;

    t.insert(&root);    

    node = t.find('A');
    t.insert('B', node);
    t.insert('C', node);
    t.insert('D', node);

    node = t.find('B');
    t.insert('E', node);
    t.insert('F', node);

    node = t.find('E');
    t.insert('K', node);
    t.insert('L', node);

    node = t.find('C');
    t.insert('G', node);

    node = t.find('D');
    t.insert('H', node);
    t.insert('I', node);
    t.insert('J', node);

    node = t.find('H');
    t.insert('M', node);

    cout << t.count() << endl;
    cout << t.height() << endl;
    cout << t.degree() << endl;

    return 0;
}

 

 

3、树形结构的层次遍历

如何按层次遍历通用树结构中的每一个数据元素? 

当前的事实 

       -树是非线性的数据结构,树的结点没有固定的编号方式 

新的需求 

       -为通用树结构提供新的方法,快速遍历每一个结点 

设计思路(游标) 

       -在树中定义一个游标(GTreeNode<T>* ) 

       -遍历开始前将游标指向根结点( root() ) 

       -获取游标指向的数据元素 

       -通过结点中的 child 成员移动游标 

              提供一组遍历相关的函数,按层次访问树中的数据元素。

      

层次遍历算法 

            -原料: class LinkQueue<T>; 

            -游标: LinkQueue<T>::front(); 

            -思想

                        begin()    → 根结点压入队列中 (游标此时指向根结点)

                        current()  → 访问队头元素指向的数据元素 (获得游标指向的队首元素值)

                        next()      → 队头元素弹出,将队头元素的孩子压入队列中(核心) 

                        end()       → 判断队列是否为空 

层次遍历算法示例 

        

编程实验 

通用树结构的层次遍历

GTree.h

template < typename T >
class GTree : public Tree<T>
{
protected:
    // ...
    LinkQueue< GTreeNode<T>*> m_queue;  // 队列中保存的值为指向通用树结点的指针  
  
    GTree(const GTree<T>&); // 通用树结构对象不能够被复制 
    GTree<T>& operator = (const GTree<T>&);  
    
public:
    // ...

    // 删除树结点,要清空队列
    SharedPointer< Tree<T> > remove(const T& value)  
    {  
        GTree<T>* ret = NULL;  
  
        GTreeNode<T>* node = find(value);  
          
        if(node == NULL)  
        {  
            THROW_EXCEPTION(InvalidParameterException, "Can not find the node via parameter value ...");  
        }  
        else  
        {  
            remove(node, ret);  
  
            m_queue.clear();  
        }  
  
  
        return ret;  
    }  
    SharedPointer< Tree<T> > remove(TreeNode<T>* node)  
    {  
        GTree<T>* ret = NULL;  
      
        node = find(node);  
  
        if(node == NULL)  
        {  
            THROW_EXCEPTION(InvalidParameterException, "Parameter node is invalid ...");  
        }  
        else  
        {  
            remove(dynamic_cast<GTreeNode<T>*>(node), ret);  
  
            m_queue.clear();  
        }  
  
        return NULL;  
    }  


    bool begin()  
    {  
        bool ret = (root() != NULL);  
  
        if(ret)  
        {  
            m_queue.clear(); // 万一上次遍历还没结束又调用一次begin,保证begin调用时队列只有根结点
            m_queue.add(root());  // 根结点入队列
        }  
  
        return ret;  
    }  
  
    bool end()  
    {  
        return (m_queue.length() == 0); // 队列无元素,遍历结束
    }  
  
    // 移动游标
    bool next()  
    {  
        bool ret = (m_queue.length() > 0);  
  
        if( ret )  
        {  
            GTreeNode<T>* node = m_queue.front();   // node指向队头元素  
  
            m_queue.remove();   // 游标移动指向下一个结点  
  
            for(node->child.move(0); !node->child.end(); node->child.next())  
            {  
                m_queue.add(node->child.current()); // 将node孩子加入队列  
            }  
  
        }  
  
        return ret;  
    }  
  
    T current()  
    {  
        if( !end() )  // 遍历没结束才有意义  
        {  
            return m_queue.front()->value;  // 队头元素值
        }  
        else  
        {  
            THROW_EXCEPTION(InvalidOperationException, "No value in current position ...");  
        }  
}  
    // clear时,会销毁所有树结点,同时清空队列
    void clear()  
    {  
        free(root());  
        this->m_root = NULL;  
  
        m_queue.clear();  
    } 
};    

同样在GTreeNode.h中

protected新增

GTreeNode(const GTreeNode<T>& );     // GTreeNode不能被复制
GTreeNode<T>& operator = (const GTreeNode<T>&);  

main.cpp

#include <iostream>
#include "GTree.h"

using namespace std;
using namespace DTLib;


int main()
{
    GTree<char> t;
    GTreeNode<char>* node = NULL;

    GTreeNode<char> root;

    root.value = 'A';
    root.parent = NULL;

    t.insert(&root);

    node = t.find('A');
    t.insert('B', node);
    t.insert('C', node);
    t.insert('D', node);

    node = t.find('B');
    t.insert('E', node);
    t.insert('F', node);

    node = t.find('E');
    t.insert('K', node);
    t.insert('L', node);
 
    node = t.find('C');
    t.insert('G', node);

    node = t.find('D');
    t.insert('H', node);
    t.insert('I', node);
    t.insert('J', node);

    node = t.find('H');
    t.insert('M', node);



    for(t.begin(); !t.end(); t.next())
    {
        cout << t.current() << "  ";
    }


    return 0;
}

   

 

4、小结 

树是一种非线性的数据结构 

结点拥有唯一前驱(父结点)和若干后继(子结点) 

树的结点包含一个数据及若干指其它结点的指针 

树与结点在程序中表现为特殊的数据类型 

查找操作是树的关键操作之— 

基于数据元素的查找可判断值是否存在于树中 

基于结点的查找可判断树中是否存在指定结点 

插入操作和删除操作都依赖于查找操作 

插入操作是构建树的唯—操作 

执行插入操作时必须指明结点间的父子关系 

插入操作必须正确处理指向父结点的指针 

插入数据元素时需要从堆空间中创建结点 

清除操作用于销毁树中的每个结点 

销毁结点时需要决定是否释放对应的内存空间 

工厂模式可用于“定制”堆空间中的结点 

只有销毁定制结点的时候需要进行释放 

删除操作各目标结点所代表的子树移除 

删除操作必须完善处理父结点和子结点的关系 

删除操作的返回值为指向树的智能指针对象 

数中返回堆中的对象时,使用智能指针作为返回值 

树的结点没有固定的编号方式 

可以按照层次关系对树中的结点进行遍历 

通过游标的思想设计遍历成员函数 

遍历成员函数是相互依赖,相互配合的关系 

遍历算法的核心为队列的使用 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值