在我前面的一篇博文資料結構之紅/黑樹狀結構中,提到了資料結構之可視化的重要性。
因為如果我們能將二叉樹可視化,也能提高加快調試,減少bug。原文給出了print_tree函數,會列印二叉樹,但是旋轉了90度,不優美,不友好。最近學習了下Graphviz這個軟體,學了下怎麼用這個軟體繪製圖片,基本解決了二叉樹的可視化問題。
我還是以我的紅/黑樹狀結構代碼為例子,寫測試代碼,看下如何將我們插入產生的紅/黑樹狀結構可視化。
我們看下代碼:
typedef unsigned long long ULL;void __tree2dot(struct rbtree_node* node,FILE* stream){ if(node->color == RB_BLACK) { fprintf(stream,"%llu [shape=box];n",*(ULL*)(node->key)); } if(node->left) { fprintf(stream," %llu -> %llu;n",*(ULL*)(node->key),*(ULL*)(node->left->key)); __tree2dot(node->left,stream); } if(node->right) { fprintf(stream," %llu -> %llu;n",*(ULL*)(node->key),*(ULL*)(node->right->key)); __tree2dot(node->right,stream); }}int tree2dot(struct rbtree* tree,char* filename){ assert(tree != NULL && filename != NULL); FILE* stream = fopen(filename,"w+"); if(stream == NULL) { fprintf(stderr, "open failed n"); return -1; } fprintf(stream,"digraph {n"); __tree2dot(tree->root,stream); fprintf(stream,"}n"); fclose(stream); return 0; }
tree2dot接受一個二叉樹和一個檔案名稱作為入參。負責建立檔案 關閉檔案和填寫dot檔案有向圖的格式頭和尾。
__tree2dot是遞迴調用,添加父節點到子節點的有向串連。我們因為是紅/黑樹狀結構,所以添加了這部分處理紅黑結點的代碼:
if(node->color == RB_BLACK) { fprintf(stream,"%llu [shape=box];n",*(ULL*)(node->key)); }
這部分代碼的作用對於黑節點,採用box類型表示節點,對於紅節點,採用預設的橢圓圖形。
比較才能看到進步,我們比較下新舊兩種方法的輸出:
在看下產生的dot檔案(Ubuntu下用XDot開啟)
下面這種的優越性,一目瞭然。這個方法目前有個缺點是沒有將NULL節點處理,導致421看不出是386左孩子還是右孩子,我們改進下:
void process_null_node(struct rbtree_node* node, int nullcount, FILE* stream){ fprintf(stream, " null%d [shape=hexagon];n", nullcount); fprintf(stream, " %llu -> null%d;n",*(ULL*)(node->key), nullcount);}void __tree2dot(struct rbtree_node* node,FILE* stream){ static int null_node_cnt = 0; if(node->color == RB_BLACK) { fprintf(stream,"%llu [shape=box];n",*(ULL*)(node->key)); } if(node->left) { fprintf(stream," %llu -> %llu;n",*(ULL*)(node->key),*(ULL*)(node->left->key)); __tree2dot(node->left,stream); } else { process_null_node(node,null_node_cnt++,stream); } if(node->right) { fprintf(stream," %llu -> %llu;n",*(ULL*)(node->key),*(ULL*)(node->right->key)); __tree2dot(node->right,stream); } else { process_null_node(node,null_node_cnt++,stream); }}
我們將NULL節點處理成六邊形,這樣就能完整的看出紅/黑樹狀結構的情況了,請看產生的dot檔案:
完整的代碼在我的github上 ,可以去下面路徑去取:
https://github.com/manuscola/rbtree
參考文獻:
1 Drawing graphs with dot