【c++数据结构与算法分析】模板类二叉树横竖打印算法详解


测试函数如下:

#include<iostream>
#include"BST.h"

using namespace std;
int main() {
	BST<int>bst;
	bst.add(23);
	bst.add(11);
	bst.add(56);
	bst.add(5);
	bst.add(20);
	bst.add(30);
	bst.add(89);
	bst.add(77);
	bst.add(45);
	bst.add(50);
	bst.graphHorizontal(cout);
	bst.afterOrder(cout);
	bst.foreorder(cout);
	bst.inorder(cout);
	bst.levelorder(cout);
	bst.graphVertical(cout);
	bst.Delete(45);
	bst.Delete(50);
	cout << endl;
	bst.graphHorizontal(cout);
	bst.afterOrder(cout);
	bst.foreorder(cout);
	bst.inorder(cout);
	bst.levelorder(cout);
	bst.graphVertical(cout);
	return 0;
}

模板类二叉树横向打印

横向打印二叉树原函数:

void graphHorizontal(ostream& out)const;//打印

横向打印二叉树实现函数

template<typename DataType>
inline void BST<DataType>::graphHorizontal(ostream& out) const
{
	horizontalGraphAux(out, 0, myRoot);
}

horizontalGraphAux打印辅助函数()

为打印辅助函数,利用递归算法更简单有效实现打印效果

template<typename DataType>
inline void BST<DataType>::horizontalGraphAux(ostream& out, int indent, BST<DataType>::BinNodePointer subtreeRoot) const
{
	if (subtreeRoot != 0) {
		horizontalGraphAux(out, indent + 8, subtreeRoot->right);
		out << setw(indent) << " " << subtreeRoot->data << endl;
		//setw当后面紧跟着的输出字段长度小于 n 的时候,在该字段前面用空格补齐,当输出字段长度大于 n 时,全部整体输出。
		horizontalGraphAux(out, indent + 8, subtreeRoot->left);
	}
}

运用递归算法,你可以想象将一颗二叉树向左旋转90度,即他的最右叶子成为了顶,我们递归一次相当于下调一层,就要将输出的空格加8个单位,递归到最右子节点后,利用setw()函数进行打印输出

setw() 函数只对紧接着的输出产生作用。
当后面紧跟着的输出字段长度小于 n 的时候,在该字段前面用空格补齐,当输出字段长度大于 n 时,全部整体输出。链接

打印效果如图:在这里插入图片描述

模板类二叉树竖向打印

总体思想:二叉树的横向打印可以一行一行来打,你可以想象,把一棵二叉树压扁之后,他是什么样子?例如这张图:
在这里插入图片描述从左往右压,把他压扁,再旋转90度,它就成了中序遍历的模样,即他是一个从小到大的排列,5 11 20 23 30 45 50 56 77 89,把一棵有序的二叉树扁平化就是中序遍历排列。每一行打印的数的位置,就是他在中序遍历排列中所在的位置,树根23 他在中序排列中在第4个位置,那么在打印第一行树根的时候,就空3个space,在输出23,第二行第三行,也是如此道理。

所以我们基于这个,反推过去,从中序遍历入手,来打印一棵二叉树

纵向打印原函数

void graphVertical(ostream& out)const;//打印

纵向打印实现函数

template<typename DataType>
inline void BST<DataType>::graphVertical(ostream& out) const
{
	vector<DataType> v;
	verticalGraghAux(myRoot, v);
	//对中序排列的动态数组vector进行初始化
	queue<BST<DataType>::BinNodePointer> q;
	q.push(myRoot);
	while (!q.empty()) {
		vector<BST<DataType>::BinNodePointer> cache;
		//把处在同一行的节点拉出来
		while (!q.empty()) { cache.push_back(q.front()); q.pop(); }
		map<DataType, int> elementMap;
		for (auto p : cache) {
			if (p) {
				//找当前节点中序遍历位置
				//使用迭代器,find方法遍历中序排列vector,找到元素所在的下标,即每一个元素,他在中序排列里的位置
				typename vector<DataType>::iterator iter;
				iter = find(v.begin(), v.end(), p->data);
				int index = iter - v.begin();
				//index即是p->data在vector数组中的下标
				elementMap.insert(pair<DataType, int>(p->data, index));
				//孩子节点入队
				if (p->left) q.push(p->left);
				if (p->right) q.push(p->right);
			}
		}
		typename map<DataType, int>::iterator iter;
		int gap = 0;
		for (iter = elementMap.begin(); iter != elementMap.end(); iter++)
		{
			for (int i = 0; i < iter->second-gap; i++) {
				out << " ";
			}
			cout << iter->first;
			gap = iter->second;
		}
		out << endl;
	}

}

一步一步来看,首先我们要得到一个中序排列。

纵向打印辅助函数(得到中序排列)

这里我们采用函数调用,得到一个中序排列的vector
对二叉树内部进行递归遍历,到叶子处,将叶子放入模板vector中

template<typename DataType>
inline void BST<DataType>::verticalGraghAux(BST<DataType>::BinNodePointer ptr, vector<DataType>& elementVector)const
{
	if (ptr != 0) {
		verticalGraghAux(ptr->left, elementVector);
		elementVector.push_back(ptr->data);
		verticalGraghAux(ptr->right, elementVector);
	}
}
//找当前节点中序遍历位置
				//使用迭代器,find方法遍历中序排列vector,找到元素所在的下标,即每一个元素,他在中序排列里的位置
				typename vector<DataType>::iterator iter;
				iter = find(v.begin(), v.end(), p->data);
				int index = iter - v.begin();
				//index即是p->data在vector数组中的下标

我们再用一个map集合来确定每一个元素在中序遍历排列中的位置,
如 5->0 11->1 20->2 23->3 30->4等等
再通过遍历map来打印每一行,map的value就是他在一行中的位置
在这里插入图片描述

实验结果如图:
在这里插入图片描述

写程序Tip

  1. map插入方法:map.insert(pair<int, string>(1, "student_one"));
  2. map遍历方法:
map<int, string>::iterator iter;
for (iter = map.begin(); iter != map.end(); iter++) {
	cout << iter->first << ' ' << iter->second << endl;
}
  1. map遍历方法:
    因为是对模板vector的使用需要在头部加入typename
    typename map<DataType, int>::iterator iter;不然会报错。
“iterator”: 类型 从属名称的使用必须以“typename”为前缀
  1. 对于函数调用返回值是数组之类的问题解决:链接

不足之处请见谅。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值