数据结构与算法14: 利用Graphviz快速可视化树(visualizing Trees with Graphviz easily )

来源:互联网 发布:淘宝水果许可证怎么办 编辑:程序博客网 时间:2024/05/16 06:44

可视化树的算法有很多,这里我们借助Graphviz实现二叉搜索树BST(二叉树)和B-Tree(多叉树)的快速可视化。关于一般的算法,可以到知网或者万方数据中去查阅,不在此处详谈。

基本的思想就是利用Gphviz可视化树的基本思想,将树转换为dot文件,然后利用Graphviz将其转换为图片。这种方法的特点就是,简单,快速,高效。这种方法可以用于辅助理解程序运行过程、调试程序。
请在使用前确认,你的系统已经正确安装了Graphviz,并配置了dot命令。
下面的程序依赖于命令:

dot example.dot -Tpng -o 1.png

完整的代码地址,可以在github BST和github BTree得到。
下面给出一些例子,文章末尾也摘取了c++实现的自动转换代码。

1. 例子

遍历可视化

将转换的图片,链接成gif图片,即可得到运行时动态效果:

先序遍历:
这里写图片描述
广度优先遍历:
这里写图片描述

AVL可视化

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

DSW算法可视化

这里写图片描述
这里写图片描述
这里写图片描述
省略中间过程,创建主链为:
这里写图片描述
中间过程省略,则最终生成的平衡BST为:
这里写图片描述

Splay Tree例子此处省略。

B-Tree可视化

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

注意下面的代码,给出了转换部分的实现;转换部分依赖于你的节点的定义,你可以根据需要调整。在你程序中合适地方插入toPng函数来生成图片。

2. 二叉树可视化实现

定义文件:

/** * 二叉树搜索树结点类BSTNode *  暂时处理整型数据 */class BSTNode {public:          BSTNode(const int& e,BSTNode*p ,BSTNode *l=0,BSTNode *r=0):key(e),height(1) ,parent(p),                  left(l),right(r){          }          std::string toString() const{ // 用于调试                std::ostringstream oss;                oss << key;                return oss.str();          }private:          int key;          int height;       // 以这个结点为根的树的高度          BSTNode *parent,*left,*right;          friend class BiTreePrinter;   // 将BST转换为图片的类};/** * BST边类 */struct BSTEdge {    BSTEdge(const std::string &f,const std::string &t,bool vis=true): from(f), to(t), isVisiable(vis) {    }    std::string toString() {        if(isVisiable)            return from+"->"+to;        else            return from+"->"+to+"[weight=100 style=invis]";    }    std::string from;    std::string to;    bool isVisiable;};/** * 利用绘制二叉树 * 1)将树形写入.dot文件 * 2)利用Graphviz程序转换.dot文件为png图片 *    dot转换为图片阶段 系统必须安装有graphviz并配置有dot命令 否则失效 *    利用系统命令例如: system ("dot example.dot -Tpng -o 1.png");转换为图片 *    关于Graphviz更多内容参见:http://www.graphviz.org */class BiTreePrinter {public:    static void toPng(const BST *bst,const std::string &desp=std::string(),const BSTNode* pcur=0);    static std::string fontColor,fillColor,currentFillColor,currentFontColor,edgeColor,arrowheadType,width,height,fontsize; // 参数    static std::string prefix;  // 文件名前缀    static long fileCounter;    //文件编号private:    static void addEdge(std::vector<std::string> &invisNodeVec,std::vector<BSTEdge> &edgeVec,const BSTNode* from);    static void writePng(std::vector<std::string> &invisNodeVec,std::vector<BSTEdge> &edgeVec,const std::string& desp,const BSTNode* pcur=0);    static std::string getNextFilename();};

实现文件:

/* * treeprinter.cpp * *  Created on: 2015年5月21日 *      Author: wangdq */#include <cstdlib>         // std::system#include <queue>#include <iostream>#include <fstream>#include <sstream>#include "treeprinter.h"std::string BiTreePrinter::fontColor="black",BiTreePrinter::fillColor="#FFFFFF",                BiTreePrinter::currentFillColor="red",BiTreePrinter::width="0.5",                BiTreePrinter::height="0.5",BiTreePrinter::fontsize="16",BiTreePrinter::currentFontColor="black",                BiTreePrinter::edgeColor="blue",BiTreePrinter::arrowheadType="normal";long BiTreePrinter::fileCounter = 1;std::string BiTreePrinter::prefix="BST";/** * 将绑定的BST转换为图片 * desp为描述字符串 * pcur指向当前节点 */void BiTreePrinter::toPng(const BST *bst,const std::string &desp,const BSTNode* pcur){    if(bst == 0) {        std::cerr << "Printer not bind to any BST"<<std::endl;        return;    }    if(bst->isEmpty())        return;    std::vector<std::string> invisNodeVec;    std::vector<std::string> visNodeVec;    std::vector<BSTEdge> edgeVec;    std::queue<const BSTNode *> nodeQueue;    nodeQueue.push(bst->getRoot());    while(!nodeQueue.empty()) {     // 广度优先遍历       const BSTNode * current = nodeQueue.front();       nodeQueue.pop();       if(current->left != 0)           nodeQueue.push(current->left);       if(current->right != 0)           nodeQueue.push(current->right);       if(current->left != 0  || current->right != 0)           addEdge(invisNodeVec,edgeVec,current);    }    writePng(invisNodeVec,edgeVec,desp,pcur);}/** * 添加结点from相关的边 */void BiTreePrinter::addEdge(std::vector<std::string> &invisNodeVec,std::vector<BSTEdge> &edgeVec,const BSTNode* from){    std::string fromId = from->toString();    std::string virtualId=std::string("v")+fromId;       if(from->left == 0 && from->right == 0)           return;       invisNodeVec.push_back(virtualId);       if(from->left != 0 && from->right != 0) {           edgeVec.push_back(BSTEdge(fromId,from->left->toString()));           edgeVec.push_back(BSTEdge(fromId,virtualId,false));           edgeVec.push_back(BSTEdge(fromId,from->right->toString()));       }else if(from->left == 0) {           edgeVec.push_back(BSTEdge(fromId,virtualId,false));           edgeVec.push_back(BSTEdge(fromId,from->right->toString()));       }else {           edgeVec.push_back(BSTEdge(fromId,from->left->toString()));           edgeVec.push_back(BSTEdge(fromId,virtualId,false));       }}/** * 写入dot文件并转换为png * 可以根据需要修改此部分参数 */void BiTreePrinter::writePng(std::vector<std::string> &invisNodeVec,std::vector<BSTEdge> &edgeVec,const std::string& desp,const BSTNode* pcur) {        long count = fileCounter;        std::string filename = getNextFilename();        std::ofstream stream(filename.c_str());        // print author and contact info        stream << "/************************************************" <<std::endl                    << "Auto generated by my program which transfer Binary Search Tree to dot file." << std::endl                    << "Author: wangdq "  <<std::endl                    <<  "Time: 2015-05-30" <<std::endl                    << "CSDN: http://blog.csdn.net/wangdingqiaoit"<<std::endl                    << "************************************************/" <<std::endl<<std::endl;        // print description        stream << "digraph BST {" << std::endl;        stream << "\tlabel=\"(" << count <<")\t" <<  desp << "\";"<< "labelloc=b;labeljust=center;"<<std::endl;        // print settings        stream << "\tnodesep=0.35" << std::endl                    << "\tordering=out" << std::endl                    << "\tnode[width=" << width << ",height=" <<height << ",fontsize=" << fontsize << ",fixedsize=true,style=\"filled\", fillcolor=\""                    << fillColor << "\",fontcolor=\"" << fontColor << "\"];" << std::endl                    << "\tedge[color=\"" << edgeColor << "\", arrowhead=\"" << arrowheadType << "\"];"<< std::endl;        // print invisible node        stream << "\t/* invisible nodes*/" << std::endl;        stream << " \t{ node[style=invis]" << std::endl;        for(std::vector<std::string>::iterator it = invisNodeVec.begin(); it != invisNodeVec.end();++it)                stream << "\t\t" << *it << std::endl;        stream <<  "\t}" << std::endl;        //set current node color        if(pcur != 0) {            stream << "\t/* set current node color attributes*/" << std::endl;            stream <<"\t" << pcur->toString() << "[fillcolor=\"" << currentFillColor << "\",fontcolor=\"" << currentFontColor <<"\"];"<<std::endl;        }        //print edges        stream << "\t/* edges*/" << std::endl;        for(std::vector<BSTEdge>::iterator it = edgeVec.begin(); it != edgeVec.end();++it)               stream <<"\t" << (*it).toString() << std::endl;        stream <<  "}"<< std::endl;        stream.close();        //transfer from dot file to png picture        std::string cmd("dot -Tpng");        cmd += " "+filename+" -o"+filename+".png";        std::system(cmd.c_str());        std::cout << "tree saved in file: " << filename<< std::endl;}/** * 获取下一个文件名 */std::string  BiTreePrinter::getNextFilename() {       const std::string ext = ".dot";       std::string filename(prefix);        std::ostringstream oss;        oss << fileCounter++;        filename += oss.str();        filename += ext;        return filename;}

转换的dot文件如下所示:

/************************************************Auto generated by my program which transfer Binary Search Tree to dot file.Author: wangdq Time: 2015-05-30CSDN: http://blog.csdn.net/wangdingqiaoit************************************************/digraph BST {    label="(23) search: 88";labelloc=b;labeljust=center;    nodesep=0.35    ordering=out    node[width=0.5,height=0.5,fontsize=16,fixedsize=true,style="filled", fillcolor="#FFFFFF",fontcolor="black"];    edge[color="blue", arrowhead="normal"];    /* invisible nodes*/    { node[style=invis]        v66        v44        v99        v77    }    /* set current node color attributes*/    88[fillcolor="red",fontcolor="black"];    /* edges*/    66->44    66->v66[weight=100 style=invis]    66->99    44->v44[weight=100 style=invis]    44->55    99->77    99->v99[weight=100 style=invis]    77->v77[weight=100 style=invis]    77->88}

3. B-Tree转换实现

B-Tree节点定义如下:

/** * M阶B-Tree结点 * 每个节点包含M-1个键值和M个指针 * 实际上每个结点多分配一个键值和指针用于辅助空间 */template<typename T,int M> class BTree;template<typename T,int M=5>class BTreeNode {public:        BTreeNode():parent(0),keynum(0),isLeaf(true) {            for(int i=0;i < M+1;++i)                childs[i] = 0;        }        BTreeNode(const T& k,BTreeNode *p=0) {            keys[0]=k;            parent = p;            keynum = 1;            isLeaf = true;            for(int i=0;i < M+1;++i)                 childs[i] = 0;        }        std::string toString() const{       // 用于调试            std::stringstream ss;            ss << keys[0];            return ss.str();        }private:        T keys[M];      // 键        BTreeNode *childs[M+1];     // 孩子指针        BTreeNode *parent;      // 父节点指针        int keynum;     // 键数目        bool isLeaf;        // 是否是叶子结点        friend class BTree<T,M>;        friend class BTreePrinter;  // 打印B-Tree为图片};

实现如下:

/* * btreeprinter.h * *  Created on: 2015年5月18日 *      Author: wangdq */#ifndef BTREEPRINTER_H_#define BTREEPRINTER_H_#include <string>#include <fstream>#include <sstream>#include <vector>#include "btree.h"/** * B-Tree边类 */struct BTreeEdge {    BTreeEdge(const std::string &f,const std::string &t,int fIndex,int tIndex,bool isWest=true):        from(f), fromIndex(fIndex),toIndex(tIndex),to(t),west(isWest) {    }    std::string toString() const{        std::string dir = ":se";        std::stringstream ss;        ss << fromIndex;        std::string fIndex = ss.str();        ss << toIndex;        std::string tIndex = ss.str();        if(west)            dir = std::string(":sw");        std::stringstream oss;            oss << "\"" << from<< "\":f"<< fIndex<< dir<< "->"<< "\""<< to<< "\":f"<< toIndex;        return oss.str();    }    std::string from;    int fromIndex,toIndex;    std::string to;    bool west;};/** * 利用绘制B-Tree * 1)将树形写入.dot文件 * 2)利用Graphviz程序转换.dot文件为png图片 *    dot转换为图片阶段 系统必须安装有graphviz并配置有dot命令 否则失效 *    利用系统命令例如: system ("dot example.dot -Tpng -o 1.png");转换为图片 *    关于Graphviz更多内容参见:http://www.graphviz.org */class BTreePrinter {public:    static std::string fontColor,fillColor,edgeColor,arrowheadType,width,height,fontsize; // 参数    static std::string prefix;  // 文件名前缀    static long fileCounter;    //文件编号    static std::string intToString(int i) {        std::ostringstream oss;        oss << i;        return oss.str();    }    /**     * 将btree转换为图片     * desp为描述内容     */    template<typename T,int M>    static void toPng(const BTree<T,M>* btree,const std::string &desp=std::string()) {        if(btree == 0) {                    std::cerr << "Printer not bind to any B-Tree"<<std::endl;                    return;                }                if(btree->isEmpty())                    return;                std::vector<std::string> visNodeVec;                std::vector<BTreeEdge> edgeVec;                std::queue<const BTreeNode<T,M>*> nodeQueue;                nodeQueue.push(btree->getRoot());                while(!nodeQueue.empty()) {     // 广度优先遍历                    const BTreeNode<T,M>* current = nodeQueue.front();                   nodeQueue.pop();                   if(!current->isLeaf)                       for(int i=0;i <= current->keynum;++i)                                        nodeQueue.push(current->childs[i]);                    addEdge(visNodeVec,edgeVec,current);                }                writePng(visNodeVec,edgeVec,desp);    }private:    static std::string getNextFilename() {          const std::string ext = ".dot";           std::string filename(prefix);            std::ostringstream oss;            oss << fileCounter++;            filename += oss.str();            filename += ext;            return filename;    }    template<typename T,int M>    static void addEdge(std::vector<std::string> & visNodeVec,std::vector<BTreeEdge> &edgeVec,            const BTreeNode<T,M>* from) {        if(from->keynum < 0 ) return;            std::string label = from->toString()+"[label=\"";            for(int i=0;i < from->keynum;++i) {                label += std::string("<f")+intToString(i)+"> "+intToString(from->keys[i]);                if(i != from->keynum-1)                    label += "|";            }            label+="\"];";            visNodeVec.push_back(label);            if(from->isLeaf) return;            for(int i=0;i < from->keynum;++i)                edgeVec.push_back(BTreeEdge(from->toString(),from->childs[i]->toString(),i,from->childs[i]->keynum-1,true));            edgeVec.push_back(BTreeEdge(from->toString(),from->childs[from->keynum]->toString(),                    from->keynum-1,from->childs[from->keynum]->keynum /2,false));    }    static void writePng(std::vector<std::string> & visNodeVec,std::vector<BTreeEdge> &edgeVec            ,const std::string& desp) {        long count = fileCounter;                std::string filename = getNextFilename();                std::ofstream stream(filename.c_str());                // print author and contact info                stream << "/************************************************" <<std::endl                            << "Auto generated by my program which transfer B-Tree to dot file." << std::endl                            << "Author: wangdq "  <<std::endl                            <<  "Time: 2015-06-08" <<std::endl                            << "CSDN: http://blog.csdn.net/wangdingqiaoit"<<std::endl                            << "************************************************/" <<std::endl<<std::endl;                // print description                stream << "digraph BTree {" << std::endl;                stream << "\tlabel=\"(" << count <<")\t" <<  desp << "\";"<< "labelloc=b;labeljust=center;"<<std::endl;                // print settings                stream  << "\tordering=out" << std::endl                            << "\tnode[shape=record,width=" << width << ",height=" <<height << ",fontsize=" << fontsize << ",style=\"filled\", fillcolor=\""                            << fillColor << "\",fontcolor=\"" << fontColor << "\"];" << std::endl                            << "\tedge[color=\"" << edgeColor << "\", arrowhead=\"" << arrowheadType << "\"];"<< std::endl;                //print visible node                for(std::vector<std::string>::iterator it = visNodeVec.begin(); it != visNodeVec.end();++it)                                stream << "\t\t" << *it << std::endl;                //print edges                stream << "\t/* edges*/" << std::endl;                for(std::vector<BTreeEdge>::iterator it = edgeVec.begin(); it != edgeVec.end();++it)                       stream <<"\t" << (*it).toString() << std::endl;                stream <<  "}"<< std::endl;                stream.close();                //transfer from dot file to png picture                std::string cmd("dot -Tpng");                cmd += " "+filename+" -o"+filename+".png";                std::system(cmd.c_str());                std::cout << "tree saved in file: " << filename<< std::endl;    }};std::string BTreePrinter::fontColor="black",BTreePrinter::fillColor="#FFFFFF",BTreePrinter::width="0.5",                BTreePrinter::height="0.5",BTreePrinter::fontsize="16",                BTreePrinter::edgeColor="blue",BTreePrinter::arrowheadType="normal";long BTreePrinter::fileCounter = 1;std::string BTreePrinter::prefix="B-Tree";#endif /* BTREEPRINTER_H_ */

转换成的dot文件如下所示:

/************************************************Auto generated by my program which transfer B-Tree to dot file.Author: wangdq Time: 2015-06-08CSDN: http://blog.csdn.net/wangdingqiaoit************************************************/digraph BTree {    label="(1)  initial B-Tree";labelloc=b;labeljust=center;    ordering=out    node[shape=record,width=0.5,height=0.5,fontsize=16,style="filled", fillcolor="#FFFFFF",fontcolor="black"];    edge[color="blue", arrowhead="normal"];        16[label="<f0> 16"];        3[label="<f0> 3|<f1> 8"];        22[label="<f0> 22|<f1> 25"];        1[label="<f0> 1|<f1> 2"];        5[label="<f0> 5|<f1> 6|<f2> 7"];        13[label="<f0> 13|<f1> 14|<f2> 15"];        18[label="<f0> 18|<f1> 20"];        23[label="<f0> 23|<f1> 24"];        27[label="<f0> 27|<f1> 37"];    /* edges*/    "16":f0:sw->"3":f1    "16":f0:se->"22":f1    "3":f0:sw->"1":f1    "3":f1:sw->"5":f2    "3":f1:se->"13":f1    "22":f0:sw->"18":f1    "22":f1:sw->"23":f1    "22":f1:se->"27":f1}
0 0
原创粉丝点击