如何讓資料結構視覺化?

當我們實現一個比較複雜的資料結構,比如二叉樹、圖、跳錶,Debug的時候怎麼驗證自己寫的函式對不對呢?

一個方法是將資料結構視覺化,與理論上的結果比較即可。

請出主角:

Graphviz

,帶一種解釋語言

dot

,可以用簡明的程式碼作圖。

之所以推薦這個是因為它可以

自動排版

1。 安裝

官網下載連結[1]

:http://www。graphviz。org/download/

作者系統為win10,其他作業系統應該大同小異。

安裝時需要勾選

新增環境變數

如何讓資料結構視覺化?

新增環境變數

2。 渲染

有vscode的讀者可以安裝一個vscode外掛:

如何讓資料結構視覺化?

安裝vscode外掛

安裝完成後,新建一個

。dot

檔案,右上角會出現一個渲染按鈕:

如何讓資料結構視覺化?

渲染按鈕

沒有vscode的讀者可以使用命令手動渲染:

dot -Tpng 你的程式碼檔名 -o 輸出圖片檔名

3。 編寫程式碼

一個空的dot程式碼(什麼都不渲染):

digraph {}

如何讓資料結構視覺化?

四個樣例渲染效果

如圖左上,可以使用下面程式碼,來建立一個名字為a的結點

digraph {  a}

如圖左下,透過

label

的修改可以改動結點顯示的文字。

如圖右上,透過

style = “dotted”

可以讓其外側圈變成虛線(可以用來顯示NULL結點)

程式碼中的引號疑似不是必須的,建議保留。

digraph {    a[label = “文字”, style = “dotted”]}

透過

結點名稱 -> 結點名稱

來建立一條線。

同理也可以使用

dotted

,(虛線用於連線NULL結點)。

這個

taillabel

可以在靠近第一個結點處顯示一個數值(可用於顯示結點中的一個數值)

digraph {    node1[label = “A”]    node2[label = “B”]    node1 -> node2[style = dotted, taillabel = “0”]}

tips:建議將結點的資料,也就是

value

,拿到

node[label = ]

中顯示,最突出、顯眼。

4。 坑

3。4。1 NULL補位

NULL,空指標,在C++中被定義為

0

一個正常結點缺失左或右子結點時,會使用NULL指標來填補空缺。

正是因為其自動排版的功能,會導致一些坑。

舉個例子,我們要畫一個這樣的二叉樹:

1    | ————-|       |0    沒有右孩子(NULL結點)

因為我們不能使用開頭為數字作為變數名,所以我們的命名規則為:

n + 數字

繪畫效果:

如何讓資料結構視覺化?

NULL結點補位

我們發現,如果不使用一個結點NULL來補位,樹被拉成了一個鏈。

我們只需要使用

一層NULL結點

來補位,這樣結構就渲染正常了。

3。4。2 命名

兩個結點名字相同,會被判定為一個結點:

如何讓資料結構視覺化?

結點名稱相同

雖然這種情況在二叉搜尋樹中不存在,但是這裡還是提一下。

怎麼區分不同的結點呢?我們可以透過C++程式中結點的指標來命名。

結點名稱:

n + 指標

即使兩個結點儲存內容相同,但是在電腦中的記憶體地址一定不同。

但是NULL結點都會被命名為

n0

,無法區分,怎麼辦呢?

我這裡使用的解決方法:NULL結點命名規則為

n + 葉子結點指標 + 是左子結點還是右子結點

如何讓資料結構視覺化?

AVL樣例

3。5 程式碼

這個程式碼是基於Part 1中結點的定義來實現的。

void DEBUG() { // 使用了part 2中的軟體來繪圖,除錯神器    FILE *fp = NULL;    fp = fopen(“。/output。dot”, “w”); // 。/output。dot 為輸出的檔名    fprintf(fp, “digraph {\n”);    deque q; // 前序遍歷,使用佇列實現    node *current;    q。push_back(root);    while (!q。empty()) {        current = q。front();        q。pop_front();        if (current == NULL) {            continue;        }        fprintf(fp, “\tn%d[label = \”%d\“]\n”, current, current->value);        for (int i = 0; i < 2; ++i) {            if (current->child[i]) {                fprintf(fp, “\tn%d -> n%d[style = blod]\n”, current, current->child[i]);                q。push_back(current->child[i]);            } else {                fprintf(fp, “\tnull%d%d[label = \”NULL\“, style = dotted]\n”, current, i);                fprintf(fp, “\tn%d -> null%d%d[style = dotted]\n”, current, current, i);            }        }    }    fprintf(fp, “}”);    fclose(fp);}