數據結構之樹(三十四)

        我們在前面學習了排序相關的知識,從今天開始,我們來學習數據結構中樹的相關東西。那麼什麼是樹呢?樹是一種非線性的數據結構

        樹是由 n( n >= 0 ) 個結點組成的有限集合。如果 n= 0,稱爲空樹;如果 n > 0,則:a> 有一個特定的稱之爲根(root)的結點;b> 根結點只有直接後繼,但沒有直接前驅;c> 除根以外的其它結點劃分爲 m( m >= 0 ) 個互不相交的有限集合T0, T1, … Tm-1,每個集合又是一棵樹,並且稱之爲根的子樹(sub tree)。下來我們來看看樹的示意圖,如下所示

圖片.png

        下來我們來看一個樹中的概念。它是指樹中的結點包含一個數據及若干指向子樹的分支,及誒單擁有子樹的數目稱爲結點的度。度爲 0 的結點稱爲葉結點,度不爲 0 的結點稱爲分支節點。樹的度定義爲所有節點中度的最大值。下來來看一個數的度爲 3 的示例,如下圖所示

圖片.png

        下來來介紹下樹中的前驅和後繼。結點的直接後繼稱爲該結點的孩子,相應的,該結點稱爲孩子的雙親;結點的孩子的孩子的 ... 稱爲該結點的子孫,相應的,該結點稱爲子孫的祖先;同一個雙親的孩子之間互稱兄弟。下來來看看樹的前驅和後繼的結構示意圖

圖片.png

        我們來看看樹中結點的層次,如下圖所示

圖片.png

        樹也有有序性,什麼叫樹的有序性呢?如果樹中結點的各子樹從左向右是有次序的,子樹間不能互換位置,則稱該樹爲有序樹,否則爲無序樹。示意圖如下圖所示

圖片.png

        那麼既然有樹的概念,就肯定有森林的概念。森林是由 n( n >= 0 ) 棵互不相交的樹組成的集合。那麼在樹中肯定也有一些常用的操作,如下

        1、將元素插入樹中;

        2、將元素從樹中刪除;

        3、獲取樹的結點數;

        4、獲取樹的高度;

        5、獲取樹的度;

        6、清空樹中的元素;

        7、 ......   


        樹與結點的類關係可以如下表示

圖片.png

        那麼我們下來來看看那樹和結點抽象類的具體源碼是怎樣寫的


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;
public:
    Tree() { m_root = NULL; }
    virtual bool ×××ert(TreeNode<T>* node) = 0;
    virtual bool ×××ert(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 height() const = 0;
    virtual void clear() = 0;
};

}

#endif // 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

        下來我們來看看樹和結點的存儲結構是怎麼設計的,結構圖如下

圖片.png

        設計要點:1、GTree 爲通用的樹結構,每個結點可以存在多個後繼結點;2、GTreeNode 能夠包含任意多指向後繼結點的指針;3、實現樹結構的所有操作(增,刪,查,等)。

        GTree(通用樹結構)的實現框架如下圖所示

圖片.png

        我們來看看通用樹結構(框架)是怎樣創建的,源碼如下


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 ×××ert(TreeNode<T>* node)
    {
        bool ret = true;
        
        return ret;
    }

    bool ×××ert(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


GTreeNode.h 源碼

#ifndef GTREENODE_H
#define GTREENODE_H

#include "Tree.h"

namespace DTLib
{

template < typename T >
class GTreeNode : public TreeNode<T>
{
public:
    LinkList<GTreeNode<T>*> child;
};

}

#endif // GTREENODE_H

        我們在樹的設計中,爲什麼要在每個樹節點中包含指向前驅結點的指針呢?從根結點到葉結點是非線性的數據結構,但是從葉結點到根結點是線性的數據結構(鏈表),結果如下

圖片.png

        下來我們就來一一的實現上面的查找、插入等操作

        1、查找操作

        查找的方式應分爲兩種:a> 基於數據元素值的查找:GTreeNode<T>* find(const T& value) const;b> 基於結點的查找:GTreeNode<T>* find(TreeNode<T>* node) const

            a> 基於數據元素值的查找,我們先在 protected 屬性中定義 find(node, value) 功能,在 node 爲根結點的樹中查找 value 所在的結點,實現思路如下

圖片.png

            b> 基於結點的查找,還是在 protected 屬性中定義 find(node, obj) 功能,在 node 爲根結點的樹中查找是否存在 obj 結點,實現思路如下

圖片.png

        具體查找相關源碼實現如下

#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);
                }
            }
        }

        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

        2、插入操作

        插入的方式應分爲兩種:a> 插入新結點:bool ×××ert(TreeNode<T>* node);b> 插入數據元素:bool ×××ert(const T& value, TreeNode<T>* parent)。

        那麼如何在樹中指定新結點的位置呢?問題分析:a> 樹是非線性的,無法採用下標的形式定位數據元素;b> 每一個樹結點都有唯一的前驅結點(父結點);c> 因此,必須先找到前驅節點,才能完成新結點的插入。

            a> 新結點的插入,如下圖所示

圖片.png

        插入新結點的流程如下圖所示

圖片.png

            b> 插入數據元素,流程如下圖所示

圖片.png

        下來我們來看看具體源碼是怎麼實現的

#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>
{
public:
    bool ×××ert(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 )
                    {
                        np->child.×××ert(n);
                    }
                }
                else
                {
                    THROW_EXCEPTION(INvalidOPerationException, "Invalid parent tree node ...");
                }
            }
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterException, "Parement node cannot be NULL ...");
        }

        return ret;
    }

    bool ×××ert(const T& value, TreeNode<T>* parent)
    {
        bool ret = true;
        GTreeNode<T>* node = GTreeNode<T>::NewNode();

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

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

        return ret;
    }
};

}

#endif // GTREE_H

        我們來寫點測試代碼,看看前面實現的查找和插入代碼是否正確

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

using namespace std;
using namespace DTLib;

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

    t.×××ert('A', NULL);
    
    node = t.find('A');
    t.×××ert('B', node);
    t.×××ert('C', node);
    t.×××ert('D', node);

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

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

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

    node = t.find('G');
    t.×××ert('N', node);

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

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

    const char* s = "KLFNMIJ";

    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;
}

        運行結果如下

圖片.png

        我們看到已經實現了之前定義的樹結構。

        3、清除操作

        a> 定義:void clear();將樹中的所有結點清除(釋放堆中的結點),樹中數據元素的清除如下所示

圖片.png

         b> free(node);清除 node 爲根結點的樹,釋放樹中的每一個結點,實現思路如下

圖片.png

        具體源碼實現如下

#ifndef GTREE_H
#define GTREE_H

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

namespace DTLib
{

template < typename T >
class GTree : public Tree<T>
{
protected:
    void free(GTreeNode<T>* node) const
    {
        if( node != NULL )
        {
            for(node->child.move(0); !node->child.end(); node->child.next())
            {
                free(node->child.current());
            }

            delete node;
        }
    }
public:    
    void clear()
    {
        free(root());

        this->m_root = NULL;

        m_queue.clear();
    }

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

}

#endif // GTREE_H

        測試代碼如下

#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.×××ert(&root);

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

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

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

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

    node = t.find('G');
    t.×××ert('N', node);

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

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

    t.clear();

    const char* s = "KLFNMIJ";

    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;
}

        我們來看看結果

圖片.png

        我們看到已經清空了樹。但是此時存在一個問題,那便是我們在 main 函數中是在堆上值指定的數據元素,上面的清除操作也會將這個堆中的數據元素刪除掉。這必然會導致問題,那麼對於樹中的結點來源於不同的存儲空間的話,此時我們應如何判斷堆空間中的結點並釋放?問題分析:單憑內存地址很難準確判斷具體的存儲區域,只有堆空間的內存需要主動釋放(delete),清除操作時只需要對堆中的結點進行釋放。

        此時的解決方案:工廠模式。

            i. 在 GTreeNode 中增加保護成員變量 m_flag;

            ii. 將 GTreeNode 中的 operator new 重載爲保護成員函數;

            iii. 提供工廠方法 GTreeNode<T>* NewNode();

            iv. 在工廠方法中 new 新結點並將 m_flag 設置爲 true

        樹結點的工廠模式示例如下

圖片.png

        我們來看看具體的源碼實現

#ifndef GTREENODE_H
#define GTREENODE_H

#include "Tree.h"
#include "LinkList.h"

namespace DTLib
{

template < typename T >
class GTreeNode : public TreeNode<T>
{
protected:
    bool m_flag;

    GTreeNode(const GTreeNode<T>&);
    GTreeNode<T>* operator = (const GTreeNode<T>&);

    void* operator new(unsigned int size) throw()
    {
        return Object::operator new(size);
    }
public:
    LinkList<GTreeNode<T>*> child;

    GTreeNode()
    {
        m_flag = false;
    }

    bool flag()
    {
        return m_flag;
    }

    static GTreeNode<T>* NewNode()
    {
        GTreeNode<T>* ret = new GTreeNode<T>();

        if( ret != NULL )
        {
            ret->m_flag = true;
        }

        return ret;
    }
};

}

#endif // GTREENODE_H

        在上面的 delete node 操作時外面進行 node->flag() 的判斷,如果爲 true,我們再來進行刪除。爲例方便的進行說明,我們在這塊加個調試語句,再來一個 else 語句,裏面打印出不同存儲區域的數據元素,我們來看看結果

圖片.png

        我們看到此時除了我們自己在堆上指定的 A 之外,剩下的數據元素已經全部被清除掉。

        4、刪除操作

        刪除的方式也分爲兩種:a> 基於數據元素值的刪除:SharedPointer< Tree<T> > remove(const T& value);b> 基於結點的刪除:SharedPointer< Tree<T> > remove(TreeNode<T>* node);

        刪除操作成員函數的設計要點:1、將被刪結點所代表的子樹進行刪除;2、刪除函數返回一顆堆空間的樹;3、具體返回值爲指向樹的只能指針對象。樹中結點的刪除示意如下圖所示

圖片.png

        如果當我們需要從函數中返回堆中的對象時,使用智能指針(SharedPointer)作爲函數的返回值。刪除操作功能的定義:void remove(GTreeNode<T>* node, GTree<T>*& ret);將 node 爲根結點的子樹從原來的樹中刪除,ret 作爲子樹返回(ret 指向堆空間中的樹對象)。刪除功能函數的實現思路如下

圖片.png

        具體源碼實現如下

#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();

        if( ret != NULL )
        {
            if( root() == node )
            {
                this->m_root = NULL;
            }
            else
            {
                LinkList<GTreeNode<T>*>& child = dynamic_cast<GTreeNode<T>*>(node->parent)->child;

                child.remove(child.find(node));

                node->parent = NULL;
            }

            ret->m_root = node;
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new tree ...");
        }
    }
public:
    SharedPointer< Tree<T> > remove(const T& value)
    {
        GTree<T>* ret = NULL;
        GTreeNode<T>* node = find(value);

        if( node != NULL )
        {
            remove(node, ret);

            m_queue.clear();
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterException, "Can not find the node via parament value ...");
        }

        return ret;
    }

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

        node = find(node);

        if( node != NULL )
        {
            remove(dynamic_cast<GTreeNode<T>*>(node), ret);

            m_queue.clear();
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterException, "Parament node is invalid ...");
        }

        return ret;
    }
};

}

#endif // GTREE_H

        我們來寫點測試代碼,刪除子樹 D,測試代碼如下

#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.×××ert(&root);

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

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

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

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

    node = t.find('G');
    t.×××ert('N', node);

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

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

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

    const char* s = "KLFNMIJ";

    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;
}

        我們來看看運行結果

圖片.png

        我們看到子樹 D 已經被刪除了,如果我們想用這個刪除的子樹 D,該如何做呢?將上面的測試代碼中的註釋的那行放開,將下面的 remove 註釋掉,再將下面 for 循環中的 t.find(s[i]) 改爲 p->find(s[i]),我們來看看運行結果

圖片.png

        我們看到打印出的是我們刪除的子樹 D。

        5、其他屬性操作

        a> 樹中結點的數目,功能定義:count(node);在 node 爲根結點的樹中統計結點數目,實現思路如下

圖片.png

        樹的結點數目的計算示例如下:

圖片.png

        b> 樹的高度,功能定義:height(node);獲取 node 爲根結點的樹的高度,實現思路如下

圖片.png

        樹的高度計算示例如下:

圖片.png

        c> 樹的度數,功能定義:degree(node);獲取 node 爲根結點的樹的度數,實現思路如下

圖片.png

        樹的度計算示例

圖片.png

        下來看看具體的源碼實現

#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());
            }
        }

        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;
        }

        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;
                }
            }
        }

        return ret;
    }
public:
    int degree() const
    {
        return degree(root());
    }

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

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

}

#endif // GTREE_H

        測試代碼如下

#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.×××ert(&root);

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

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

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

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

    node = t.find('G');
    t.×××ert('N', node);

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

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

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

    return 0;
}

        我們來看看運行結果

圖片.png

        6、層次遍歷

        如何按層次遍歷通用樹結構中的每一個數據元素呢?樹是非線性的數據結構,樹的結點沒有固定的編號方式。那麼我們就得提供一個新的需求,爲通用樹結構提供新的方法,能快速遍歷每一個結點。

        設計思路(遊標):a> 在樹中定義一個遊標(GTreeNode<T>*);b> 遍歷開始前將遊標指向根結點(root());c> 獲取遊標指向的數據元素;d> 通過結點中的 child 成員移動遊標。提供一組遍歷相關的函數,按層次訪問樹中的數據元素。如下

圖片.png

        層次遍歷算法:a> 原料:class LinkQueue<T>;b> 遊標:LinkQueue<T>::front();c> 思想:i. begin() --> 將根節點壓入隊列中;ii. current() --> 訪問隊頭元素指向的數據元素;iii. next() --> 隊頭元素彈出,將對頭元素的孩子壓入隊列中(核心);iv. end() --> 判斷隊列是否爲空。層次遍歷算法示例如下

圖片.png

        下來我們來看看具體的源碼實現

GTreeNode.h 源碼

#ifndef GTREENODE_H
#define GTREENODE_H

#include "Tree.h"
#include "LinkList.h"

namespace DTLib
{

template < typename T >
class GTreeNode : public TreeNode<T>
{
protected:
    bool m_flag;

    GTreeNode(const GTreeNode<T>&);
    GTreeNode<T>* operator = (const GTreeNode<T>&);

    void* operator new(unsigned int size) throw()
    {
        return Object::operator new(size);
    }
public:
    LinkList<GTreeNode<T>*> child;

    GTreeNode()
    {
        m_flag = false;
    }

    bool flag()
    {
        return m_flag;
    }

    static GTreeNode<T>* NewNode()
    {
        GTreeNode<T>* ret = new GTreeNode<T>();

        if( ret != NULL )
        {
            ret->m_flag = true;
        }

        return ret;
    }
};

}

#endif // GTREENODE_H

 GTree.h 源碼

#ifndef GTREE_H
#define GTREE_H

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

namespace DTLib
{

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:    
    GTree()
    {

    }
    
    bool begin()    
    {
        bool ret = (root() != NULL);

        if( ret )
        {
            m_queue.clear();
            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();

            m_queue.remove();

            for(node->child.move(0); !node->child.end(); node->child.next())
            {
                m_queue.add(node->child.current());
            }
        }

        return ret;
    }

    T current()
    {
        if( !end() )
        {
            return m_queue.front()->value;
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterException, "No value at current position ...");
        }
    }
};

}

#endif // GTREE_H

        那麼在 remove 的操作中也要加上相應的隊列的清除:m_queue.clear(); 測試代碼如下

#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.×××ert(&root);

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

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

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

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

    node = t.find('G');
    t.×××ert('N', node);

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

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

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

    return 0;
}

        運行結果如下

圖片.png

        我們看到已經將之前的樹結構層次遍歷了一遍。通過對樹的學習,總結如下:1、樹是一種非線性的數據結構,結點擁有唯一前驅(父結點)和若干後繼(子結點);2、樹的結點包含一個數據及若干指向其他結點的指針,樹與結點在程序中表現爲特殊的數據類型;3、基於數據元素的查找可判斷值是否存在於樹中,基於結點的查找可判斷樹中是否存在指定結點;4、插入操作是構建樹的唯一操作,執行插入操作時必須指明結點間的父子關係;5、插入操作必須正確處理指向父結點的指針,插入數據元素時需要從堆空間中創建結點;6、銷燬結點時需要決定是否釋放對應的內存空間,工廠模式可用於“定製”堆空間中的結點,只有銷燬定製結點的時候需要進行釋放;7、刪除操作必須完善處理父結點和子結點的關係,它的返回值爲指向樹的智能指針對象,函數中返回堆中的對象時使用智能指針作爲返回值;8、插入操作和刪除操作都依賴於查找操作;9、樹的結點沒有固定的編號方式,可以按照層次關係對樹中的結點進行遍歷;10、通過遊標的思想設計遍歷成員函數,遍歷成員函數是相互依賴,相互配合的關係,遍歷算法的核心爲隊列的使用。

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