直观打印二叉树的代码实现

输出思路:
将原有树结构后续遍历,生成辅助接点,记录横向位置,按照层存储。
使用后续遍历的原因是,父节点依据左右子树节点确定位置会比较合理,调整位置比较方便。
生成的时候需要注意两点:
1 如果横向位置小于0,那么已经处理过的节点位置右移。
2 如果横向位置不是当前层的合理位置(不是处于最右方),那么当前节点以及子树都需要右移。
然后遍历每一层节点,输出value
斜杠则是依据left、right的位置±1输出。

代码如下:

#ifndef _TEST_PRINT_TREE_H_
#define _TEST_PRINT_TREE_H_
#include <string.h>
#include <stdio.h>
namespace nm_test_print_tree
{
    struct NodePosInfo{
        int w ;
        int dep;
        int value;
        bool is_red;
        NodePosInfo(int w_, int dep_, int value_, int is_red_):
        w(w_),
        dep(dep_),
        value(value_),
        is_red(is_red_)
        {
        }
        shared_ptr<NodePosInfo> left;
        shared_ptr<NodePosInfo> right;
    };
    using node_pos_ptr = shared_ptr<NodePosInfo>;
    template<class NODE_TYPE>
    class tree_print{
        using node_ptr = NODE_TYPE*;
        using get_node_ptr_func_type = function<node_ptr (node_ptr)>;
        using get_node_key_func_type = function<int (node_ptr)>;
        using get_node_color_func_type = function<int (node_ptr)>;
        public:
        tree_print(
            get_node_ptr_func_type get_left, 
            get_node_ptr_func_type get_right, 
            get_node_key_func_type get_key_value, 
            get_node_color_func_type get_color = nullptr)
        :_get_left_ptr(get_left), _get_right_ptr(get_right), _get_key_value(get_key_value), _get_color_is_red(get_color){

        }
        private:
            get_node_ptr_func_type      _get_left_ptr;
            get_node_ptr_func_type      _get_right_ptr;
            get_node_key_func_type            _get_key_value;
            get_node_color_func_type           _get_color_is_red;
            map<int, vector<node_pos_ptr> > db_; //存储每一层的指针
            map<int, int > db1_; // 存储每一层最后一个
        
        public:
        void print(node_ptr n){
            // 转储到db_
            translate(n);
            // 依据db_进行打印
            for(const auto& it: db_){
                unsigned int idx = 0;
                for(const auto& v: it.second){
                    for(int i = idx+1; i< v->w; i++){
                        printf(" ");
                    }
                    if(v->is_red && _get_color_is_red)
                        printf("\033[31m%d\033[0m", v->value);
                    else
                    {
                        printf("%d", v->value);
                    }
                    
                    idx = v->w;
                    char s_value[128];
                    snprintf(s_value, sizeof(s_value), "%d", v->value);
                    idx += strlen(s_value) -1;
                }
                printf("\n");
                idx = 0;
                for(const auto& v: it.second){
                    if(v->left){
                        for(int i = idx+1; i< v->left->w + 1; i++){
                            printf(" ");
                        }
                        printf("/");
                        idx = v->left->w + 1;
                    }
                    if(v->right){
                        for(int i = idx+1; i< v->right->w - 1 ; i++){
                            printf(" ");
                        }
                        printf("\\");
                        idx = v->right->w - 1;
                    }
                }
                printf("\n");
            }
        }
        private:
        void translate(node_ptr n){
            db_.clear();
            db1_.clear();
            loop(n, 0);
        }
        node_pos_ptr loop(node_ptr n, int dep){
            if(!n)return nullptr;
            node_pos_ptr pos = make_shared<NodePosInfo>(0, dep, _get_key_value(n), _get_color_is_red?_get_color_is_red(n):0);
            
            // 先处理子节点,然后根据子节点的位置确定当前节点位置
            pos->left = loop(_get_left_ptr(n), dep + 1);
            pos->right = loop(_get_right_ptr(n), dep + 1);
            if(pos->left && pos->right){
                pos->w = (pos->left->w + pos->right->w)/2;
            }else if(_get_left_ptr(n)){
                pos->w = pos->left->w +2;
            }else if(_get_right_ptr(n)){
                pos->w = pos->right->w -2;
            }
            if(pos->w < 0){
                // 如果当前节点位置小于0 整体右移
                for(auto& it : db_){
                    for(auto& v: it.second){
                        v->w += pos->w;
                    }
                }
                for(auto& it: db1_){
                    it.second += pos->w;
                }
                pos->w = 0;
            }
            
            if(pos->w < db1_[dep]+4){
                // 如果当前节点位置不合理(当前节点位置已经被占用,则当前节点及所有子节点右移)
                tree_pos_move(pos, db1_[dep]+4-pos->w);
            }
            // 更新当前层的最大占用
            db1_[dep] = max(pos->w, db1_[dep]);
            // 存储当前节点
            db_[dep].push_back(pos);
            return pos;
        };    
        void tree_pos_move(node_pos_ptr& node, int r){
            if(!node)return;
            node->w += r;
            // 更新当前层的最大占用
            db1_[node->dep] = max(db1_[node->dep], node->w);
            if(node->left)tree_pos_move(node->left, r);
            if(node->right)tree_pos_move(node->right, r);
        }
    };
}
#endif //_TEST_PRINT_TREE_H_
// test code
    struct Node{
        shared_ptr<Node> left_;
        shared_ptr<Node> right_;
        public:
        int value_;
        Node(int v){
            value_ = v;
        }
    };
       ...
		binary_avl_tree st_avl;
        for(int it = 1; it< 20;it++){
            st_avl.insert(it);
        }
        nm_test_print_tree::tree_print<Node> tprint(
            [](Node* n){return n->left_.get();},
            [](Node* n){return n->right_.get();},
            [](Node* n){return n->value_;});
            
        tprint.print(st_avl.head.get());
        
        st_avl.erase(12);
        tprint.print(st_avl.head.get());

效果如下:

                 8
          /              \
         4                12
      /     \         /       \
     2       6       10        16
    / \     / \     / \     /     \
   1   3   5   7   9   11  14      18
                          / \     / \
                         13  15  17  19

                 8
          /             \
         4               16
      /     \         /     \
     2       6       11      18
    / \     / \     / \     / \
   1   3   5   7   10  14  17  19
                  /   / \
                 9   13  15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值