上一篇博客给出的二叉树实现不区分叶结点与分支结点,即叶结点的类中也有两个指向左右子结点的指针,只不过指针为NULL。这种设计实现简单,但是造成了空间上的浪费。这里给出一种将叶结点与分支结点实现为两个不同类的实现方式。不过这两个类都继承自同一个虚基类。
具体实现如下:
/********************************************************/
// 用模板实现二叉树(Binary Node)结点的定义
// 区分叶结点与分支结点,遍历不作为成员函数
/********************************************************/
#pragma once
// 基类
class VarBinNode_1
{
public:
// 因为无数据成员,所以也就没有定义构造函数
virtual ~VarBinNode_1() {};
virtual bool isLeaf() = 0;
};
//类模板继承普通类
// 叶结点,存储操作数的值
template<typename Operand>
class LeafNode : public VarBinNode_1
{
public:
LeafNode(Operand val) : var(val) {}
bool isLeaf() { return true; }
const Operand& value()const { return var; }
private:
Operand var; //操作数的值
};
// 分支结点,存储操作符和两个子结点的指针
template<typename Operator>
class IntlNode : public VarBinNode_1
{
public:
IntlNode(Operator val, VarBinNode_1 *l, VarBinNode_1 *r) : opx(val), left(l), right(r) {}
bool isLeaf() { return false; }
VarBinNode_1* leftchild()const { return left; }
VarBinNode_1* rightchild()const { return right; }
const Operator& value()const { return opx; }
private:
Operator opx; //操作符的值
VarBinNode_1 *left; //左右子结点,不知道具体类型,所以用的是基类
VarBinNode_1 *right;
};
// 中序遍历 左中右
template<typename Operand, typename Operator>
void traverse(VarBinNode_1* root)
{
if (root == NULL)
{
return;
}
if (root->isLeaf())
{
cout << ((LeafNode<Operand>*)root)->value();
}
else
{
// 强制类型转换。因为->的优先级太高,导致一定要多加括号
char opx = ((IntlNode<Operator>*)root)->value();
if (opx == '+' || opx == '-')
{
cout << "(";// 在表达式的什么地方加括号是个问题
}
//中序遍历
traverse<Operand, Operator>(((IntlNode<Operator>*)root)->leftchild());
cout << opx;
traverse<Operand, Operator>(((IntlNode<Operator>*)root)->rightchild());
if (opx == '+' || opx == '-')
{
cout << ")";// 在表达式的什么地方加括号是个问题
}
}
return;
}
需要注意的有:
- 叶结点和分支结点的实现使用了类模板继承普通类的方式;
- traverse()函数被实现成一个模板函数,而不是类模板的成员函数;
- traverse()为一递归函数,实现中用到了强制类型转换,因为输入参数的类型是基类指针,但是真正给他赋值的可能是叶结点指针,也可能是分支结点指针。真是由于两种类型都有可能,所以才将输入参数定为基类的指针。root进行强制类型转换后才能指向派生类特有的成员;
- 强制类型转换时,注意括号。因为->的优先级太高,必须在->前加括号。
main函数如下:
/********************************************************/
// 主函数
// 用于测试编写的各函数与数据结构
/********************************************************/
#include "Public.h"
#include "Tools.h"
#include "VarBinNode1.h"
int main()
{
/********************************************************/
// 7.2,叶结点和分支结点分开的二叉树,方案1
/********************************************************/
LeafNode<string> NodeA("4");
LeafNode<string> NodeB("x");
LeafNode<string> NodeC("2");
LeafNode<string> NodeD("x");
LeafNode<string> NodeE("a");
LeafNode<string> NodeF("c");
IntlNode<char> NodeG('*', &NodeA, &NodeB);
IntlNode<char> NodeH('*', &NodeC, &NodeD);
IntlNode<char> NodeI('+', &NodeH, &NodeE);
IntlNode<char> NodeJ('*', &NodeG, &NodeI);
IntlNode<char> root('-', &NodeJ, &NodeF);
traverse<string,char>(&root);
system("pause");
return 0;
}
这里实现的二叉树即为书上图5.9给出的例子:
实现的结果如下: