问题
如何按层次遍历通用树结构中的每一个数据元素?
当前的事实:树是非线性的数据结构,树的结点没有固定的编号方式
新的需求:为通用树结构提供新的方法,快速遍历每一个结点
设计思路(游标)
- 在树中定义一个游标 (GTreeNode<T> *)
- 遍历开始前将游标指向根结点 (root())
- 获取游标指向的数据元素
- 通过结点的 child 成员移动游标
提供一组遍历相关的函数,按层次访问树中的数据元素
函数 | 功能说明 |
begin() | 初始化,准备进行遍历访问 |
next() | 移动游标,指向下一个结点 |
current() | 获取游标所指向的数据元素 |
end() | 判断游标是否到达尾部 |
层次遍历算法
- 原料:class LinkQueue<T>;
- 游标:LinkQueue<T>::front();
思想:
- begin() → 将根结点压入队列中
- current() → 访问队头元素指向的数据元素
- next() → 队头元素弹出,将队头元素的孩子压入队列中
- end() → 判断队列是否为空
层次遍历算法示例
编程实验:通用树结构的层次遍历
文件: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;
GTreeNode() = default;
bool flag()
{
return m_flag;
}
static GTreeNode<T>* NewNode()
{
GTreeNode<T> *ret = new GTreeNode<T>();
if (ret != nullptr)
{
ret->m_flag = true;
}
return ret;
}
protected:
bool m_flag = false;
GTreeNode(const GTreeNode<T>&) = default;
GTreeNode<T>& operator = (const GTreeNode<T>&) = default;
void *operator new (unsigned int size) noexcept(true)
{
return Object::operator new(size);
}
};
}
#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>
{
public:
GTree() = default;
bool insert(TreeNode<T> *node) override
{
bool ret = true;
if (node != nullptr)
{
if (this->m_root == nullptr)
{
node->parent = nullptr;
this->m_root = node;
}
else
{
GTreeNode<T> *np = find(node->parent);
if (np != nullptr)
{
GTreeNode<T> *n = dynamic_cast<GTreeNode<T>*>(node);
if (np->child.find(n) < 0)
{
np->child.insert(n);
}
}
else
{
THROW_EXCEPTION(InvalidOpertionExcetion, "Invalid partent tree node ...");
}
}
}
else
{
THROW_EXCEPTION(InvalidParameterExcetion, "Parameter node cannot be NULL ...");
}
return ret;
}
bool insert(const T &value, TreeNode<T> *parent) override
{
bool ret = true;
GTreeNode<T> *node = GTreeNode<T>::NewNode();
if (node != nullptr)
{
node->value = value;
node->parent = parent;
insert(node);
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory to create node ...");
}
return ret;
}
SharedPointer<Tree<T>> remove(const T &value) override
{
GTree<T> *ret = nullptr;
GTreeNode<T> *node = find(value);
if (node != nullptr)
{
remove(node, ret);
m_queue.clear();
}
else
{
THROW_EXCEPTION(InvalidParameterExcetion, "can not find the node ...");
}
return ret;
}
SharedPointer<Tree<T>> remove(TreeNode<T> *node) override
{
GTree<T> *ret = nullptr;
node = find(node);
if (node != nullptr)
{
remove(dynamic_cast<GTreeNode<T>*>(node), ret);
m_queue.clear();
}
else
{
THROW_EXCEPTION(InvalidParameterExcetion, "Parameter node is invalid ...");
}
return ret;
}
GTreeNode<T>* find(const T &value) const override
{
return find(root(), value);
}
GTreeNode<T>* find(TreeNode<T> *node) const override
{
return find(root(), dynamic_cast<GTreeNode<T>*>(node));
}
GTreeNode<T>* root() const override
{
return dynamic_cast<GTreeNode<T>*>(this->m_root);
}
int degree() const override
{
return degree(root());
}
int count() const override
{
return count(root());
}
int height() const override
{
return height(root());
}
void clear() override
{
free(root());
this->m_root = nullptr;
m_queue.clear();
}
bool begin()
{
bool ret = (root() != nullptr);
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(InvalidOpertionExcetion, "No value at current position ...");
}
}
~GTree()
{
clear();
}
protected:
LinkQueue<GTreeNode<T>*> m_queue;
GTree(const GTree<T>&) = default;
GTree<T>& operator = (const GTree<T>&) = default;
GTreeNode<T> *find(GTreeNode<T>* node, const T &value) const
{
GTreeNode<T> *ret = nullptr;
if (node != nullptr)
{
if (node->value == value)
{
return node;
}
else
{
for (node->child.move(0); !node->child.end() && (ret == nullptr); node->child.next())
{
ret = find(node->child.current(), value);
}
}
}
return ret;
}
GTreeNode<T> *find(GTreeNode<T>* node, GTreeNode<T> *obj) const
{
GTreeNode<T> *ret = nullptr;
if (node == obj)
{
return node;
}
else
{
if (node != nullptr)
{
for (node->child.move(0); !node->child.end() && (ret == nullptr); node->child.next())
{
ret = find(node->child.current(), obj);
}
}
}
return ret;
}
void free(GTreeNode<T> *node)
{
if (node != nullptr)
{
for (node->child.move(0); !node->child.end(); node->child.next())
{
free(node->child.current());
}
if (node->flag())
{
delete node;
}
}
}
void remove(GTreeNode<T> *node, GTree<T> *&ret)
{
ret = new GTree<T>();
if (ret != nullptr)
{
if (node == root())
{
this->m_root = nullptr;
}
else
{
GTreeNode<T> *parent = dynamic_cast<GTreeNode<T>*>(node->parent);
parent->child.remove(parent->child.find(node));
node->parent = nullptr;
}
ret->m_root = node;
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No enough memory to create tree ...");
}
}
int count(GTreeNode<T> *node) const
{
int ret = 0;
if (node != nullptr)
{
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 != nullptr)
{
for (node->child.move(0); !node->child.end(); node->child.next())
{
int h = height(node->child.current());
if (ret < h)
{
ret = h;
}
}
ret += 1;
}
return ret;
}
int degree(GTreeNode<T> *node) const
{
int ret = 0;
if (node != nullptr)
{
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;
}
};
}
#endif // GTREE_H
文件:main.cpp
#include <iostream>
#include "GTree.h"
using namespace std;
using namespace DTLib;
int main()
{
GTree<char> t;
GTreeNode<char> *node = nullptr;
GTreeNode<char> root;
root.value = 'A';
root.parent = nullptr;
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() << " ";
}
cout << endl;
return 0;
}
输出:
A B C D E F G H I J K L M
小结
- 树的结点没有固定的编号方式
- 可以按照层次关系对树中的结点进行遍历
- 通过游标的思想设计遍历成员函数
- 遍历成员函数是相互依赖,相互配合的关系
- 遍历算法的核心是队列的使用
以上内容整理于狄泰软件学院系列课程,请大家保护原创!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。