整体思路
首先定义了打印树的迭代函数printTreeRec (),它的参数是当前节点,函数从根节点进入,判断不是叶子节点后打印其左孩子和右孩子,如此迭代,直到迭代到叶子节点。
在打印过程中为了格式化,整体上分了四类节点:
四类节点图示
- 分类1:是分类2的一种特殊情况,这是根节点没有右孩子的情况,无法调用它的父母,所以先单独判断;
- 分类2:右孩子链的结尾换行,此情况为了避免与左子树根节点的打印重合,又加入条件必须是是父节点的右孩子节点;
- 分类3:左子树开头情况,要统计tab数和“|”的打印位置;其以是否是叶子节点又分为两种情况;
- 分类4:右子树链的普通情况,也是最简单的情况,因为它在一行的中间,所以不需要有特殊输出格式;
部分代码
template<typename T>
void BinSearchTree<T>::printTreeRec(Node* tempRoot) {
//分类1:根节点没有右孩子的情况,无法调用它的父母,所以先单独判断
if(tempRoot==root && tempRoot->right==NULL)
cout << tempRoot->item << "\n";
//分类2:右孩子链的结尾换行,此情况为了避免与左子树根节点的打印重合,又加入条件必须是是父节点的右孩子节点
else if (tempRoot->right == NULL && tempRoot->parent->right==tempRoot)
cout << tempRoot->item<<"\n";
//分类3:左子树开头情况,要统计tab数和“|”的打印位置
else if (tempRoot != root && tempRoot->parent->left == tempRoot) {
Node* temp = tempRoot->parent; //创建临时节点,用来遍历当前节点的祖宗节点,判断tab数和“|”的打印位置
vector <string> tab; //创建向量tab用来储存tab和“|”
//遍历当前节点的祖宗节点
while (temp != root) {
if(temp->parent->left != NULL && temp->item != 101)
tab.push_back("│\t"); //如果此节点未被标记,则需要打印“|”连接
else
tab.push_back("\t"); //如果此节点被标记为101则说明以打印,不需要再打印“|”连接
temp = temp->parent;
}
//将tab向量反向输出
for (int i = tab.size(); i >0; i--) {
cout << tab[i-1];
}
//分类3.1:如果这个节点既是左子树开始,又是左子树结尾
if (tempRoot->right == NULL) {
cout << "└─────\t" << tempRoot->item << "\n";
tempRoot->item = 101; //标记已输出的右子树根
}
//分类3.2:如果这个节点仅是左子树开始,他还有右孩子
else {
cout << "└─────\t" << tempRoot->item << "────\t";
tempRoot->item = 101; //标记已输出的右子树根
}
}
else cout << tempRoot->item <<"────\t"; //分类4:右子树链的普通情况
//递归代码
if (tempRoot->right != NULL) {
printTreeRec(tempRoot->right);
}
if (tempRoot->left != NULL) {
printTreeRec(tempRoot->left);
}
if (tempRoot->right == NULL && tempRoot->left == NULL) return;
}
template<typename T>
void BinSearchTree<T>::printTree()
{
printTreeRec(root);
}
实验总结
在打印时最重要的是给二叉树的节点进行合理而全面的情况分析。在本实验中我将其分为4类节点,因为是横向打印二叉树,所以右孩子的打印不需要换行,只需要考虑这个节点是否还有右孩子,有的话继续打印,没有则进行换行操作,并回溯到其父节点判断是否有左孩子。整个遍历过程是一个深度优先搜索。
其中比较复杂的情况是每行打印的第一个左孩子需要考虑到tab数和“│”的打印位置。对于这种情况,建立一个vector向量进行数据储存,每次换行后从该节点向根回溯,遇到没有输出的左孩子储存“│\t”,其他则只储存“\t”,然后进行倒序输出,并将自身标记为101表示已输出(随机生成树的节点最大值为100的情况),后面不用再打印“│”符号了。
其中比较容易出bug的地方是根节点和节点的双重身份(例如既是左孩子又是一行的最后一个),为此我分了4个判断语句进行准确的分类,排除了这些bug。