在我前面的一篇博文資料結構之紅黑樹中,提到了資料結構之視覺化的重要性。 因為如果我們能將二叉樹視覺化,也能提高加快除錯,減少bug。原文給出了print_tree函式,會列印二叉樹,但是旋轉了90度,不優美,不友好。最近學習了下Graphviz這個軟體,學了下怎麼用這個軟體繪製圖片,基本解決了二叉樹的視覺化問題。
我還是以我的紅黑樹程式碼為例子,寫測試程式碼,看下如何將我們插入產生的紅黑樹視覺化。
我們看下程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
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是遞迴呼叫,新增父節點到子節點的有向連線。我們因為是紅黑樹,所以新增了這部分處理紅黑結點的程式碼:
1 2 3 4 |
if(node->color == RB_BLACK) { fprintf(stream,"%llu [shape=box];n",*(ULL*)(node->key)); } |
這部分程式碼的作用對於黑節點,採用box型別表示節點,對於紅節點,採用預設的橢圓圖形。
比較才能看到進步,我們比較下新舊兩種方法的輸出:
在看下生成的dot檔案(Ubuntu下用XDot開啟)
下面這種的優越性,一目瞭然。這個方法目前有個缺點是沒有將NULL節點處理,導致421看不出是386左孩子還是右孩子,我們改進下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
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