动手构建二叉树的可视化工具

前言

承接N叉树的可视化,可能二叉树的可视化比N叉树的用途更广,这里就不再赘述其原理了,只简单做了注释,原理和N叉树差不多,只是需要特殊处理空节点。

对原理和实现感兴趣的可以跳转至前文

绘制效果展示:
图片-1653229888470

代码实现

注意:二叉可视化树的节点需要自己构建和初始化。
//数据结构:
typedef struct GraphBinaryNode{
    GraphBinaryNode* leftChild;
    GraphBinaryNode* rightChild;
    int leftLength;
    int rightLength;
    string content;
    int pos;
}*GraphBinaryTree

//二叉可视化树的初始化:
void calLength(GraphBinaryTree root){
    if(root->leftChild != nullptr){
        calLength(root->leftChild);
        root->leftLength = root->leftChild->leftLength + root->leftChild->rightLength + 1;
    }else{
    	//一边为空,但父节点信息的绘制需要占据空间
        root->leftLength = root->content.size() / 2 + 1;
    }
    if(root->rightChild != nullptr){
        calLength(root->rightChild);
        root->rightLength = root->rightChild->leftLength + root->rightChild->rightLength + 1;
    }else{
    	//一边为空,但父节点信息的绘制需要占据空间
        root->rightLength = root->content.size() / 2 + 1;
    }
}

void calPosition(GraphBinaryTree root){
    int pos = root->pos;
    if(root->leftChild != nullptr){
        root->leftChild->pos = pos;
        calPosition(root->leftChild);
    }
    //隔断所占的空间
    pos += root->leftLength + 1;
    if(root->rightChild != nullptr){
        root->rightChild->pos = pos;
        calPosition(root->rightChild);
    }
}

//绘制节点信息及辅助线:
string drawNode(GraphBinaryNode* node){
    int length, offset = node->content.size() / 2 + 1;;
    string blank;
    if(node->leftChild != nullptr && node->rightChild != nullptr){
        length = node->leftLength + node->rightLength + 1;
        if(length / 2 - offset > 0){
            blank = string(length / 2 - offset, ' ');
        }
    }else if(node->leftChild == nullptr){
        blank = "";
    }else if(node->rightChild == nullptr){
        if(node->leftLength - offset > 0){
            blank = string(node->leftLength - offset, ' ');
        }
    }
    return blank + "(" + node->content + ")";
}

string drawLine(GraphBinaryNode* node){
    if(node->leftChild == nullptr && node->rightChild == nullptr) return "";
    string line;
    if(node->leftChild != nullptr){
        string blank, segment;
        GraphBinaryNode* leftChild = node->leftChild;
        //辅助线的绘制比较复杂,不仅取决于孩子的情况,还取决于孙子的情况,需要枚举多个情况
        if((leftChild->leftChild != nullptr && leftChild->rightChild != nullptr)
        || (leftChild->leftChild == nullptr && leftChild->rightChild == nullptr)){
            blank = string(node->leftLength / 2, ' ');
            segment = string(node->leftLength / 2, '-');
        }else if(leftChild->leftChild == nullptr){
            blank = string(leftChild->content.size() / 2 + 1, ' ');
            segment = string(leftChild->rightLength, '-');
        }else if(leftChild->rightChild == nullptr){
            blank = string(node->leftLength - leftChild->content.size() / 2 - 2, ' ');
            segment = string(leftChild->rightLength, '-');
        }
        line += blank + '+' + segment;
    }else{
        line += string(node->leftLength, ' ');
    }
    line += '-';
    if(node->rightChild != nullptr){
        string blank, segment;
        GraphBinaryNode* rightChild = node->rightChild;
        //辅助线的绘制比较复杂,不仅取决于孩子的情况,还取决于孙子的情况,需要枚举多个情况
        if((rightChild->leftChild != nullptr && rightChild->rightChild != nullptr)
        || (rightChild->leftChild == nullptr && rightChild->rightChild == nullptr)){
            blank = string(node->rightLength / 2, ' ');
            segment = string(node->rightLength / 2, '-');
        }else if(rightChild->leftChild == nullptr){
            blank = string(rightChild->rightLength, ' ');
            segment = string(rightChild->content.size() / 2 + 1, '-');
        }else if(rightChild->rightChild == nullptr){
            blank = string(rightChild->leftLength - rightChild->content.size() / 2, ' ');
            segment = string(node->rightLength - rightChild->content.size() / 2 - 2, '-');
        }
        line += segment + '+' + blank;
    }else{
        line += string(node->rightLength, ' ');
    }
    return line;
}

//绘制主函数:
string drawGraphTree(GraphBinaryTree root){
    calLength(root);
    calPosition(root);

    vector<GraphBinaryNode*> level{root};
    string graphTree;
    while(level.size() > 0){
        string row1, row2;
        vector<GraphBinaryNode*> nextLevel;
        for(GraphBinaryNode* node : level){
            string nodeInfo = drawNode(node);
            string line = drawLine(node);

            string fix1, fix2;
            if(row1.size() < node->pos){
                fix1 = string(node->pos - row1.size(), ' ');
            }
            if(row2.size() < node->pos){
                fix2 = string(node->pos - row2.size(), ' ');
            }
            nodeInfo = fix1 + nodeInfo;
            line = fix2 + line;

            if(node->leftChild != nullptr) nextLevel.push_back(node->leftChild);
            if(node->rightChild != nullptr) nextLevel.push_back(node->rightChild);

            row1 += nodeInfo;
            row2 += line;
        }
        level = nextLevel;
        row1 += '\n';
        row2 += '\n';
        graphTree += row1 + row2;
    }
    return graphTree;
}

int main(){
    //对于构建好的二叉树
    GraphBinaryTree root;
    //构建二叉树...
    cout << drawGraphTree(root) << endl;
    return 0;
}
上一篇 下一篇

评论 | 0条评论