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