数据结构开发(20):树中结点的查找插入清除与删除操作
Posted pylearn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构开发(20):树中结点的查找插入清除与删除操作相关的知识,希望对你有一定的参考价值。
0.目录
1.树中结点的查找操作
2.树中结点的插入操作
3.树中结点的清除操作
4.树中结点的删除操作
5.小结
1.树中结点的查找操作
查找的方式:
- 基于数据元素值的查找
GTreeNode<T>* find(const T& value) const
- 基于结点的查找
GTreeNode<T>* find(TreeNode<T>* node) const
树中数据元素和结点的查找:
基于数据元素值的查找:
- 定义功能:find(node, value)
- 在 node 为根结点的树中查找 value 所在的结点
在GTree.h中实现基于数据元素值的查找:
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;
}
public:
GTreeNode<T>* find(const T& value) const
{
return find(root(), value);
}
基于结点的查找:
- 定义功能:find(node, obj)
- 在 node 为根结点的树中查找是否存在 obj 结点
在GTree.h中实现基于结点的查找:
protected:
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(TreeNode<T>* node) const
{
return find(root(), dynamic_cast<GTreeNode<T>*>(node));
}
2.树中结点的插入操作
插入的方式:
- 插入新结点
bool insert(TreeNode<T>* node)
- 插入数据元素
bool insert(const T& value, TreeNode<T>* parent)
问题:
- 如何指定新结点在树中的位置?
问题分析:
- 树是非线性的,无法采用下标的形式定位数据元素
- 每一个树结点都有唯一的前驱结点 ( 父结点 )
- 因此,必须先找到前驱结点,才能完成新结点的插入
新结点的插入:
插入新结点:
插入数据元素:
在GTree.h中实现插入操作:
public:
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 )
{
np->child.insert(n);
}
}
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 != NULL )
{
node->value = value;
node->parent = parent;
insert(node);
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new tree node ...");
}
return ret;
}
mian.cpp测试这棵树:
#include <iostream>
#include "GTree.h"
using namespace std;
using namespace StLib;
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(‘G‘);
t.insert(‘N‘, 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;
}
运行结果为:
K E B A
L E B A
F B A
G C A
M H D A
I D A
J D A
3.树中结点的清除操作
清除操作的定义:
- void clear()
- 将树中的所有结点清除 ( 释放堆中的结点 )
树中数据元素的清除:
清除操作功能的定义:
- free(node)
- 清除 node 为根结点的树
- 释放树中的每一个结点
在GTree.h中实现清除操作:
protected:
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;
}
}
public:
void clear()
{
free(root());
this->m_root = NULL;
}
问题:
- 树中的结点可能来源于不同的存储空间,如何判断堆空间中的结点并释放?
问题分析
- 单凭内存地址很难准确判断具体的存储区域
- 只有堆空间的内存需要主动释放 ( delete )
- 清除操作时只需要对堆中的结点进行释放
解决方案:工厂模式
- 在 GTreeNode 中增加保护成员变量 m_flag
- 将 GTreeNode 中的 operator new 重载为保护成员函数
- 提供工厂方法 GTreeNode
- 在工厂方法中 new 新结点并将 m_flag 设置为 true
树结点的工厂模式示例:
实现树结点的工厂模式(修改GTreeNode.h和GTree.h中的对应代码):
GTreeNode.h
#ifndef GTREENODE_H
#define GTREENODE_H
#include "Tree.h"
#include "LinkList.h"
namespace StLib
{
template <typename T>
class GTreeNode : public TreeNode<T>
{
protected:
bool m_flag;
void* operator new(size_t 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中的对应代码:
protected:
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;
}
}
}
public:
bool insert(const T& value, TreeNode<T>* parent)
{
bool ret = true;
GTreeNode<T>* node = GTreeNode<T>::NewNode();
if( node != NULL )
{
node->value = value;
node->parent = parent;
insert(node);
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new tree node ...");
}
return ret;
}
4.树中结点的删除操作
删除的方式:
- 基于数据元素值的删除
SharedPointer< Tree<T> > remove(const T& value)
- 基于结点的删除
SharedPointer< Tree<T> > remove(TreeNode<T>* node)
删除操作成员函数的设计要点:
- 将被删结点所代表的子树进行删除
- 删除函数返回一棵堆空间中的树
- 具体返回值为指向树的智能指针对象
树中结点的删除:
实用的设计原则:
- 当需要从函数中返回堆中的对象时,使用智能指针 ( SharedPointer ) 作为函数的返回值。
删除操作功能的定义:
void remove(GTreeNode<T>* node, GTree<T>*& ret)
- 将 node 为根结点的子树从原来的树中删除
- ret 作为子树返回 ( ret 指向堆空间中的树对象 )
删除功能函数的实现:
在GTree.h中实现删除操作:
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.remove(child.find(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;
}
main.cpp测试
#include <iostream>
#include "GTree.h"
using namespace std;
using namespace StLib;
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(‘G‘);
t.insert(‘N‘, 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);
SharedPointer< Tree<char> > p = t.remove(t.find(‘D‘));
const char* s = "KLFGMIJ";
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;
}
运行结果为:
M H D
I D
J D
5.小结
- 查找操作是树的关键操作之一
- 基于数据元素的查找可判断值是否存在于树中
- 基于结点的查找可判断树中是否存在指定结点
- 插入操作和删除操作都依赖于查找操作
- 插入操作是构建树的唯一操作
- 执行插入操作时必须指明结点间的父子关系
- 插入操作必须正确处理指向父结点的指针
- 插入数据元素时需要从堆空间中创建结点
- 清除操作用于销毁树中的每个结点
- 销毁结点时需要决定是否释放对应的内存空间
- 工厂模式可用于“定制”堆空间中的结点
- 只有销毁定制结点的时候需要进行释放
- 删除操作将目标结点所代表的子树移除
- 删除操作必须完善处理父结点和子结点的关系
- 删除操作的返回值为指向树的智能指针对象
- 函数中返回堆中的对象时,使用智能指针作为返回值
最终的GTreeNode.h和GTree.h代码:
GTreeNode.h
#ifndef GTREENODE_H
#define GTREENODE_H
#include "Tree.h"
#include "LinkList.h"
namespace StLib
{
template <typename T>
class GTreeNode : public TreeNode<T>
{
protected:
bool m_flag;
void* operator new(size_t 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"
namespace StLib
{
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;
}
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;
}
}
}
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.remove(child.find(node));
node->parent = NULL;
}
ret->m_root = node;
}
}
public:
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 )
{
np->child.insert(n);
}
}
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 = GTreeNode<T>::NewNode();
if( node != NULL )
{
node->value = value;
node->parent = parent;
insert(node);
}
else
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new tree node ...");
}
return ret;
}
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;
}
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);
}
int degree() const
{
return 0;
}
int count() const
{
return 0;
}
int height() const
{
return 0;
}
void clear()
{
free(root());
this->m_root = NULL;
}
~GTree()
{
clear();
}
};
}
#endif // GTREE_H
以上是关于数据结构开发(20):树中结点的查找插入清除与删除操作的主要内容,如果未能解决你的问题,请参考以下文章