之前的博客http://blog.csdn.net/socho/article/details/51565498
《對(duì)"堆"的理解》中打印堆是在終端中,不是很直觀,發(fā)現(xiàn)一個(gè)可以生成圖片的利器graphviz,于是用c語(yǔ)言調(diào)用下,程序運(yùn)行中生成圖片,特別直觀了。
以上是用終端打印出來(lái)的,發(fā)現(xiàn)了一個(gè)繪圖利器graphviz,可以把堆畫(huà)出來(lái)。安裝這個(gè)軟件什么的就不講了,自己百度,這里默認(rèn)已經(jīng)安裝好了。所以,對(duì)以上代碼更新
#include <stdio.h>
#define N 12
void printHeap(int A[], char *filename, int color_point){
///// 這里是在終端打印節(jié)點(diǎn) /////
printf(" -----------%d-----------\n", A[1]);
printf(" / \\\n");
printf(" ----%d---- ----%d----\n", A[2], A[3]);
printf(" / \\ / \\\n");
printf(" --%d-- ---%d--- ---%d %d\n", A[4], A[5], A[6], A[7]);
printf(" / \\ / \\ /\n");
printf("%d %d %d %d %d\n", A[8], A[9], A[10], A[11] , A[12]);
///// 以下是輸出到heap_*.dot文件中,供用graphicvz生成png圖片節(jié)點(diǎn)表示法用 /////
int i = 0;
FILE* fp = fopen(filename, "w+"); //dot -Tpng heap.dot -o heap.png
if( NULL == fp)
{
fprintf(stderr, "打開(kāi)文件描述符失敗\n");
return;
}
fprintf(fp, "digraph {\n");
for ( i = 1; i <= N/2; i++)
{
if (i*2 <= N)
{
fprintf(fp, " %llu -> %llu\n", A[i], A[i*2]) ;
} else {
fprintf(fp, " %llu -> null [style = dotted]\n", A[i]); // 設(shè)置null節(jié)點(diǎn)連接線(xiàn)的屬性
}
if (i*2+1 <= N){
fprintf(fp, " %llu -> %llu\n", A[i], A[i*2+1]);
} else {
fprintf(fp, " %llu -> null [style = dotted]\n", A[i]);
}
}
if (0 != color_point) // 需要染色的節(jié)點(diǎn)
{
fprintf(fp, ";\n %d [color = red]\n", color_point);
}
fprintf(fp, ";\n null [style = dotted]}\n"); // 設(shè)置null節(jié)點(diǎn)的屬性
fclose(fp);
}
void swap(int A[], int t, int i){
A[t] = A[t] ^ A[i];
A[i] = A[t] ^ A[i];
A[t] = A[i] ^ A[t];
}
/* 向下調(diào)整。
* @param A 堆的線(xiàn)性存儲(chǔ)方式,數(shù)組
* @param n 節(jié)點(diǎn)總數(shù)
* @param i 需要向下調(diào)整的節(jié)點(diǎn)編號(hào)
*/
void godown(int A[], int n, int i) //傳入一個(gè)需要向下調(diào)整的結(jié)點(diǎn)編號(hào)i,這里傳入1,即從堆的頂點(diǎn)開(kāi)始向下調(diào)整
{
int t,flag=0,count=0;//flag用來(lái)標(biāo)記是否需要繼續(xù)向下調(diào)整
//當(dāng)i結(jié)點(diǎn)有兒子的時(shí)候(其實(shí)是至少有左兒子的情況下)并且有需要繼續(xù)調(diào)整的時(shí)候循環(huán)窒執(zhí)行
while( i*2<=n && flag==0 )
{
//首先判斷他和他左兒子的關(guān)系,并用t記錄值較小的結(jié)點(diǎn)編號(hào)
if( A[i] > A[i*2] )
t=i*2;
else
t=i;
//如果他有右兒子的情況下,再對(duì)右兒子進(jìn)行討論
if(i*2+1 <= n)
{
//如果右兒子的值更小,更新較小的結(jié)點(diǎn)編號(hào)
if(A[t] > A[i*2+1])
t=i*2+1;
}
//如果發(fā)現(xiàn)最小的結(jié)點(diǎn)編號(hào)不是自己,說(shuō)明子結(jié)點(diǎn)中有比父結(jié)點(diǎn)更小的
if(t!=i)
{
swap(A, t, i);//交換
i=t;//更新i為剛才與它交換的兒子結(jié)點(diǎn)的編號(hào),便于接下來(lái)繼續(xù)向下調(diào)整
count++;
printf("\n\n第 %d 次調(diào)整堆:\n", count);
char filename[10];
sprintf(filename, "heap%d.dot", count);
printHeap(A, filename, 17);
}
else
flag=1;//則否說(shuō)明當(dāng)前的父結(jié)點(diǎn)已經(jīng)比兩個(gè)子結(jié)點(diǎn)都要小了,不需要在進(jìn)行調(diào)整了,退出
}
}
int main(){
int A[N+1] = { 0, 2, 7, 24, 11, 67, 35, 25, 16, 19, 97, 80, 50}; // 這里為了直接展示調(diào)整堆算法,直接給出已經(jīng)建好堆的數(shù)組,免去建堆這一步,A[0]是不使用的,堆從A[1]開(kāi)始。
printf("初始化堆:\n");
printHeap(A, "heap.dot", 0);
A[1] = 17; // 測(cè)試,修改堆頂值
printf("\n\n修改堆頂值為17:\n");
printHeap(A, "heap0.dot", 17);
godown(A, N, 1);
return 0;
}
編譯gcc heap.c -o heap
然后執(zhí)行./heap
可以看到同之前一樣也在打印出堆,不過(guò)這次多生成了一些東西,在當(dāng)前目錄下發(fā)現(xiàn)了heap.dot、heap0.dot、heap1.dot、heap2.dot、heap3.dot幾個(gè)文件,然后在當(dāng)前目錄下寫(xiě)一個(gè)腳本dot.sh,編輯腳本,內(nèi)容如下
#!/bin/sh
dot -Tpng heap.dot -o heap.png
dot -Tpng heap0.dot -o heap0.png
dot -Tpng heap1.dot -o heap1.png
dot -Tpng heap2.dot -o heap2.png
dot -Tpng heap3.dot -o heap3.png
dot是graphviz的命令,記得要把這個(gè)燃盡安裝上哦。
然后,執(zhí)行腳本./dot.sh
然后就發(fā)現(xiàn)生成了heap.png、heap0.png、heap1.png、heap2.png、heap3.png,分別是上面終端打印出幾種形態(tài)的圖片版本,例如
初始堆
堆頂換成17
堆頂下移到左孩子
17再下移到左孩子
17再下移到左孩子
始于2016-06-08,杭州;更新至2016-06-12,杭州。