c++二叉树打印(只为美观)

文章目录

  • 先上效果图
  • 然后上代码
    • 测试用例:
  • 思路
  • 写在最后

先上效果图

c++二叉树打印(只为美观)_第1张图片

然后上代码

	/**
	 * 中序遍历返回节点数组
	 * @param root 根节点
	 * @return 中序遍历节点数组
	 */
	std::vector<TreeNode *> inorderTraversal(TreeNode *root) {
	    std::vector<TreeNode *> res;
	    std::stack<TreeNode *> stk;
	    while (root != nullptr || !stk.empty()) {
	        while (root != nullptr) {
	            stk.push(root);
	            root = root->left;
	        }
	        root = stk.top();
	        stk.pop();
	        res.push_back(root);
	        root = root->right;
	    }
	    return res;
	}
    /**
     * 利用下划线和正反斜杠打印出美观的二叉树,没有破坏二叉树结构,但传入的root会有变化
     * @param root  二叉树根节点
     */
    void printTree(TreeNode *root) {
        if (!root)return;
        auto tmp = root;
        std::vector<TreeNode *> intv = inorderTraversal(tmp);//中序遍历节点数组
        std::string template_str;//模板string,表示每行打印string的长度
        int location = 0;
        std::unordered_map<TreeNode *, int> first_locations;//存储节点对应在本行string中的首位置
        for (auto &i : intv) {
            location = template_str.size();
            template_str += std::to_string(i->val) + " ";
            first_locations[i] = location;
        }
        for (auto &i:template_str)i = ' ';//把模板全部置为空格方便后续使用
        //层序遍历
        std::queue<TreeNode *> q;
        q.push(root);
        while (!q.empty()) {
            int currentLevelSize = q.size();
            int cur_loc = 0;
            std::string tmp_str1 = template_str, tmp_str2 = template_str;//1为节点所在行,2为其下一行
            for (int i = 1; i <= currentLevelSize; ++i) {
                auto node = q.front();
                q.pop();
                cur_loc = first_locations[node];
                std::string num_str = std::to_string(node->val);
                //左边,如果存在左孩子,那么在第二行对应位置打印'/',在第一行补上'_'
                if (node->left) {
                    q.push(node->left);
                    int first_loc = first_locations[node->left] + 1;
                    tmp_str2[first_loc++] = '/';
                    while (first_loc < cur_loc)tmp_str1[first_loc++] = '_';

                }
                //中间,对应位置打印节点值(有可能为多位数)
                for (int j = 0; j < num_str.length(); ++j, ++cur_loc) {
                    tmp_str1[cur_loc] = num_str[j];
                }
                //右边,如果存在右孩子,那么在第二行对应位置打印'\',在第一行补上'_'
                if (node->right) {
                    q.push(node->right);
                    int last_loc = first_locations[node->right] - 1;
                    tmp_str2[last_loc] = '\\';
                    while (cur_loc < last_loc) {
                        tmp_str1[cur_loc++] = '_';
                    }
                }
            }
            //打印两行
            std::cout << tmp_str1 << std::endl;
            std::cout << tmp_str2 << std::endl;
        }
    }

测试用例:

tools.hpp

#include 
#include "iostream"
#include "stdlib.h"
#include "vector"
#include "cmath"
#include "algorithm"
#include "map"
#include "fstream"
#include "queue"
#include "unordered_map"
#include "unordered_set"
#include "stack"


//Definition for a binary tree node.
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;

    TreeNode() : val(0), left(nullptr), right(nullptr) {}

    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}

    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

namespace tools {
// Binary tree*********************************************************
    /**
     * 中序遍历返回节点数组
     * @param root 根节点
     * @return 中序遍历节点数组
     */
    std::vector<TreeNode *> inorderTraversal(TreeNode *root) {
        std::vector<TreeNode *> res;
        std::stack<TreeNode *> stk;
        while (root != nullptr || !stk.empty()) {
            while (root != nullptr) {
                stk.push(root);
                root = root->left;
            }
            root = stk.top();
            stk.pop();
            res.push_back(root);
            root = root->right;
        }
        return res;
    }

    void trimLeftTrailingSpaces(std::string &input) {
        input.erase(input.begin(), find_if(input.begin(), input.end(), [](int ch) {
            return !isspace(ch);
        }));
    }

    void trimRightTrailingSpaces(std::string &input) {
        input.erase(find_if(input.rbegin(), input.rend(), [](int ch) {
            return !isspace(ch);
        }).base(), input.end());
    }

    TreeNode *stringToTreeNode(std::string input) {
        trimLeftTrailingSpaces(input);
        trimRightTrailingSpaces(input);
        input = input.substr(1, input.length() - 2);
        if (!input.size()) {
            return nullptr;
        }

        std::string item;
        std::stringstream ss;
        ss.str(input);

        getline(ss, item, ',');
        TreeNode *root = new TreeNode(stoi(item));
        std::queue<TreeNode *> nodeQueue;
        nodeQueue.push(root);

        while (true) {
            TreeNode *node = nodeQueue.front();
            nodeQueue.pop();

            if (!getline(ss, item, ',')) {
                break;
            }

            trimLeftTrailingSpaces(item);
            if (item != "null") {
                int leftNumber = stoi(item);
                node->left = new TreeNode(leftNumber);
                nodeQueue.push(node->left);
            }

            if (!getline(ss, item, ',')) {
                break;
            }

            trimLeftTrailingSpaces(item);
            if (item != "null") {
                int rightNumber = stoi(item);
                node->right = new TreeNode(rightNumber);
                nodeQueue.push(node->right);
            }
        }
        return root;
    }
    
    /**
     * 利用下划线和正反斜杠打印出美观的二叉树,没有破坏二叉树结构,但传入的root会有变化
     * @param root  二叉树根节点
     */
    void printTree(TreeNode *root) {
        if (!root)return;
        auto tmp = root;
        std::vector<TreeNode *> intv = inorderTraversal(tmp);//中序遍历节点数组
        std::string template_str;//模板string,表示每行打印string的长度
        int location = 0;
        std::unordered_map<TreeNode *, int> first_locations;//存储节点对应在本行string中的首位置
        for (auto &i : intv) {
            location = template_str.size();
            template_str += std::to_string(i->val) + " ";
            first_locations[i] = location;
        }
        for (auto &i:template_str)i = ' ';//把模板全部置为空格方便后续使用
        //层序遍历
        std::queue<TreeNode *> q;
        q.push(root);
        while (!q.empty()) {
            int currentLevelSize = q.size();
            int cur_loc = 0;
            std::string tmp_str1 = template_str, tmp_str2 = template_str;//1为节点所在行,2为其下一行
            for (int i = 1; i <= currentLevelSize; ++i) {
                auto node = q.front();
                q.pop();
                cur_loc = first_locations[node];
                std::string num_str = std::to_string(node->val);
                //左边,如果存在左孩子,那么在第二行对应位置打印'/',在第一行补上'_'
                if (node->left) {
                    q.push(node->left);
                    int first_loc = first_locations[node->left] + 1;
                    tmp_str2[first_loc++] = '/';
                    while (first_loc < cur_loc)tmp_str1[first_loc++] = '_';

                }
                //中间,对应位置打印节点值(有可能为多位数)
                for (int j = 0; j < num_str.length(); ++j, ++cur_loc) {
                    tmp_str1[cur_loc] = num_str[j];
                }
                //右边,如果存在右孩子,那么在第二行对应位置打印'\',在第一行补上'_'
                if (node->right) {
                    q.push(node->right);
                    int last_loc = first_locations[node->right] - 1;
                    tmp_str2[last_loc] = '\\';
                    while (cur_loc < last_loc) {
                        tmp_str1[cur_loc++] = '_';
                    }
                }
            }
            //打印两行
            std::cout << tmp_str1 << std::endl;
            std::cout << tmp_str2 << std::endl;
        }
    }

}

main.cpp

#include "tools.hpp"
int main() {
    tools::printTree(tools::stringToTreeNode("[1,1,1,1,1,1,1,11,3,213,21,13,21,31,321,321,654,684,1,87,94,1,1687,941,4,984,21,65,4987]"));
    return 0;
}

运行结果如前面效果图。

思路

首先中序遍历得到每个节点在每行的位置
比如这个树长这样
c++二叉树打印(只为美观)_第2张图片那么它的中序遍历就是4 2 5 1 10 3;
按中序遍历的顺序逐个插入节点值字符串空格,得到字符串"4 2 5 1 10 3 ";
如果每行的节点都按上述字符串的位置摆放,是不会出现矛盾的,而且预留了'\'、'/'、'_'的位置。

//中序遍历模板string
4 2 5 1 10 3 
//按行按模板摆放
      1 
  2        3 
4   5   10   
//增加符号
    __1___
   /      \
  2       _3 
 / \     /
4   5   10   

写在最后

为啥写这个,完全是因为在网上没找到类似的程序,可能是我关键词没搜索对吧;
还有就是闲的,很久不写博客都快忘了这个东西了;
最后就是我又想造福大众了,点个赞让我知道这篇文章帮过你。

你可能感兴趣的:(日记,c++,二叉树)