数据结构学习笔记9——区分叶结点与分支结点的二叉树实现方案一

上一篇博客给出的二叉树实现不区分叶结点与分支结点,即叶结点的类中也有两个指向左右子结点的指针,只不过指针为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;
}

需要注意的有:

  1. 叶结点和分支结点的实现使用了类模板继承普通类的方式;
  2. traverse()函数被实现成一个模板函数,而不是类模板的成员函数;
  3. traverse()为一递归函数,实现中用到了强制类型转换,因为输入参数的类型是基类指针,但是真正给他赋值的可能是叶结点指针,也可能是分支结点指针。真是由于两种类型都有可能,所以才将输入参数定为基类的指针。root进行强制类型转换后才能指向派生类特有的成员;
  4. 强制类型转换时,注意括号。因为->的优先级太高,必须在->前加括号。

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给出的例子:



实现的结果如下:



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值