前面实现了通用树结构,介绍了树到二叉树的转换,这节实现二叉树中的结点查找、插入
目录
1、 二叉树的存储结构设计
2、二叉树中的结点查找操作
3、二叉树中的结点插入操作
目标:完成二叉树和二叉树结点的存储结构设计
设计要点
- BTree为二叉树结构,每个结点最多只有两个后继结点
- BTreeNode只包含4个固定的公有成员(哪4个?)
- 实现树结构的所有操作(增,删,查,等)
注意我们研究的是二叉树的实际工程应用,在实际工程中加个parent指针是十分必要的
BTreeNode的设计与实现
template < typename T >
class BTreeNode : public TreeNode
{
public:
BTreeNode* left;
BTreeNode* right;
// factory pattern
// ...
};
发现 GTreeNode 和 BTreeNode 都使用了工厂模式,所以将会对之前代码重构
BTree的设计与实现
GTree 和 BTree 都不能对象间的赋值
BTree( 二叉树结构 ) 的实现架构
编程实验
二叉树结构的创建 BTree.h BTreeNode.h
TreeNode.h (重构:将new对象的标记放在TreeNode中,在new BTreeNode 和 GTreeNode时都会做标记)
#ifndef TREENODE_H
#define TREENODE_H
#include "Object.h"
namespace DTLib
{
template < typename T >
class TreeNode : public Object
{
protected:
bool m_flag;
TreeNode(const TreeNode& );
TreeNode& operator = (const TreeNode&);
void* operator new (unsigned int size) throw()
{
return Object::operator new(size);
}
public:
T value;
TreeNode* parent;
TreeNode()
{
m_flag = false;
parent = NULL;
}
bool flag()
{
return m_flag;
}
virtual ~TreeNode() = 0;
};
template < typename T >
TreeNode::~TreeNode()
{
}
}
#endif // TREENODE_H
BTreeNode.h
#ifndef BTREENODE_H
#define BTREENODE_H
#include "TreeNode.h"
namespace DTLib
{
template < typename T >
class BTreeNode : public TreeNode
{
public:
BTreeNode* left;
BTreeNode* right;
BTreeNode()
{
left = NULL;
right = NULL;
}
static BTreeNode* NewNode()
{
BTreeNode* ret = new BTreeNode();
if( ret )
{
ret->m_flag = true;
}
return ret;
}
};
}
#endif // BTREENODE_H
BTree.h
#ifndef BTREE_H
#define BTREE_H
#include "Tree.h"
#include "BTreeNode.h"
#include "Exception.h"
#include "LinkQueue.h"
namespace DTLib
{
template < typename T >
class BTree : public Tree
{
public:
bool insert(TreeNode* node)
{
bool ret = true;
return ret;
}
bool insert(const T& value,TreeNode* parent)
{
bool ret = true;
return ret;
}
SharedPointer< Tree > remove(const T& value)
{
return NULL;
}
SharedPointer< Tree > remove(TreeNode* node)
{
return NULL;
}
BTreeNode* find(const T& value) const
{
return NULL;
}
BTreeNode* find(TreeNode* node) const
{
return NULL;
}
BTreeNode* root() const
{
return dynamic_cast*>(this->m_root);
}
int degree() const
{
return 0;
}
int count() const
{
return 0;
}
int height() const
{
return 0;
}
void clear()
{
this->m_root = NULL;
}
~BTree()
{
clear();
}
};
}
#endif // BTREE_H
main.cpp
#include
#include "BTree.h"
using namespace std;
using namespace DTLib;
int main()
{
BTree bt;
BTreeNode btn;
return 0;
}
查找的方式 (和通用树查找方式完全一致)
-基于数据元素值的查找
BTreeNode<T>* find(const T& value) const
-基于结点的查找
BTreeNode<T>* find(TreeNode<T>* node) const
树中数据元素和结点的查找
递归的操作
基于数据元素值的查找
-定义功能: find(node, value)
功能:在node为根结点的二叉树中查找value所在的结点
基于结点的查找
-定义功能: find(node, obj)
功能:在node为根结点的二叉树中查找是否存在obj结点
编程实验
基于数据元素值的查找,基于结点的查找 BTree.h
#ifndef BTREE_H
#define BTREE_H
#include "Tree.h"
#include "BTreeNode.h"
#include "Exception.h"
#include "LinkQueue.h"
namespace DTLib
{
template < typename T >
class BTree : public Tree
{
protected:
virtual BTreeNode* find(BTreeNode* node, const T& value) const
{
BTreeNode* ret = NULL;
if(node != NULL)
{
if(node->value == value)
{
ret = node;
}
else
{
if(ret == NULL)
{
ret = find(node->left, value);
}
if(ret == NULL) // 左子树没找到,开始找右子树
{
ret = find(node->right, value);
}
}
}
return ret;
}
virtual BTreeNode* find(BTreeNode* node, BTreeNode* obj) const
{
BTreeNode* ret = NULL;
if(node == obj)
{
ret = node;
}
else
{
if(node != NULL)
{
if(ret == NULL)
{
ret = find(node->left, obj);
}
if(ret == NULL)
{
ret = find(node->right, obj);
}
}
}
return ret;
}
public:
// ...
BTreeNode* find(const T& value) const
{
return find(root(), value);
}
BTreeNode* find(TreeNode* node) const
{
return find(root(), dynamic_cast*>(node));
}
BTreeNode* root() const
{
return dynamic_cast*>(this->m_root);
}
// ...
};
}
#endif // BTREE_H
需要考虑的问题
是否能够在二叉树的任意结点处插入子结点?
是否需要指定新数据元素(新结点)的插入位置?
二叉树结点的位置枚举类型
enum BTNodePos
{
ANY, // 插入左右都可以
LEFT, // 左边插入
RIGHT // 右边插入
};
插入的方式
-插入新结点
bool insert(TreeNode
bool insert(TreeNode
-插入数据元素
bool insert(const T& value, TreeNode
bool insert(const T& value, TreeNode
新结点的插入
5的左子树位置不能插入,2都不能插入
指定位置的结点插入
● 新结点n作为目标父结点np的孩子插入,插入位置为pos
插入新结点
● 新结点node插入node指定的父结点下pos位置
插入数据元素
● 新数据元素value作为目标父结点parent的孩子插入到pos位置
编程实验
二叉树的插入操作 insert
BTreeNode.h 增加枚举常量
#ifndef BTREENODE_H
#define BTREENODE_H
#include "TreeNode.h"
namespace DTLib
{
enum BTNodePos
{
ANY,
LEFT,
RIGHT
};
template < typename T >
class BTreeNode : public TreeNode
{
// ...
};
}
#endif // BTREENODE_H
BTree.h
#ifndef BTREE_H
#define BTREE_H
#include "Tree.h"
#include "BTreeNode.h"
#include "Exception.h"
#include "LinkQueue.h"
namespace DTLib
{
template < typename T >
class BTree : public Tree
{
protected:
// ...
// 新结点n作为目标父结点np的孩子插入,插入位置为pos
virtual bool insert(BTreeNode* n, BTreeNode* np, BTNodePos pos)
{
bool ret = true;
if( pos == ANY )
{
if(np->left == NULL)
{
np->left = n;
}
else if(np->right == NULL)
{
np->right = n;
}
else
{
ret = false;
}
}
else if( pos == LEFT )
{
if(np->left == NULL)
{
np->left = n;
}
else
{
ret = false;
}
}
else if( pos == RIGHT )
{
if(np->right == NULL)
{
np->left = n;
}
else
{
ret = false;
}
}
else
{
ret = false;
}
return ret;
}
public:
bool insert(TreeNode* node)
{
return insert(node, ANY);
}
// 新结点node插入node指定的父结点下pos位置
virtual bool insert(TreeNode* node, BTNodePos pos)
{
bool ret = true;
if(node != NULL)
{
if(this->m_root == NULL) // 没有根结点,node作为根结点
{
node->parent = NULL;
this->m_root = node;
}
else
{
BTreeNode* np = find(node->parent);
if(np != NULL) // node指定的父结点存在于当前树中
{
ret = insert(dynamic_cast*>(node), np, pos);
}
else
{
THROW_EXCEPTION(InvalidParameterException, "Invalid parent tree node ...");
}
}
}
else
{
THROW_EXCEPTION(InvalidParameterException, "Parameter node can not be NULL ...");
}
return ret;
}
bool insert(const T& value, TreeNode* parent)
{
return insert(value, parent, ANY);
}
// 新数据元素value作为目标父结点parent的孩子插入pos位置
virtual bool insert(const T& value, TreeNode* parent, BTNodePos pos)
{
bool ret = true;
BTreeNode* node = BTreeNode::NewNode(); // 创建新结点
if(node == NULL)
{
THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new node ...");
}
else
{
node->value = value; // 指定目标元素值
node->parent = parent; // 指定目标父结点
ret = insert(node, pos);
/* 插入失败 */
if( !ret )
{
delete node; //
}
}
return ret;
}
// ...
};
}
#endif // BTREE_H
main.cpp
#include
#include "BTree.h"
using namespace std;
using namespace DTLib;
int main()
{
BTree bt;
BTreeNode* n = NULL;
bt.insert(1, NULL);
n = bt.find(1);
bt.insert(2, n);
bt.insert(3, n);
n = bt.find(2);
bt.insert(4, n);
bt.insert(5, n);
n = bt.find(4);
bt.insert(8, n);
bt.insert(9, n);
n = bt.find(5);
bt.insert(10, n);
n = bt.find(3);
bt.insert(6, n);
bt.insert(7, n);
n = bt.find(6);
bt.insert(11, n, LEFT);
int a[] = {8, 9, 10, 11, 7}; // 叶结点值
for(int i=0; i<5; i++)
{
TreeNode* node = bt.find(a[i]);
while( node ) // 遍历单链表
{
cout << node->value << " ";
node = node->parent;
}
cout << endl;
}
return 0;
}
小结
二叉树的插入操作需要指明插入的位置
插入操作必须正确处理指向父结点的指针
插入数据元素时需要从堆空间中创建结点
当数据元素插入失败时需要释放结点空间