目录
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、小结
树是一种非线性的数据结构
结点拥有唯一前驱(父结点)和若干后继(子结点)
树的结点包含一个数据及若干指其它结点的指针
树与结点在程序中表现为特殊的数据类型
查找操作是树的关键操作之—
基于数据元素的查找可判断值是否存在于树中
基于结点的查找可判断树中是否存在指定结点
插入操作和删除操作都依赖于查找操作
插入操作是构建树的唯—操作
执行插入操作时必须指明结点间的父子关系
插入操作必须正确处理指向父结点的指针
插入数据元素时需要从堆空间中创建结点
清除操作用于销毁树中的每个结点
销毁结点时需要决定是否释放对应的内存空间
工厂模式可用于“定制”堆空间中的结点
只有销毁定制结点的时候需要进行释放
删除操作各目标结点所代表的子树移除
删除操作必须完善处理父结点和子结点的关系
删除操作的返回值为指向树的智能指针对象
数中返回堆中的对象时,使用智能指针作为返回值
树的结点没有固定的编号方式
可以按照层次关系对树中的结点进行遍历
通过游标的思想设计遍历成员函数
遍历成员函数是相互依赖,相互配合的关系
遍历算法的核心为队列的使用