二叉樹的直觀顯示
當我們學習樹這種資料結構時會牽扯到很多的東西,基本上學習資料結構的一大重心都圍繞著樹這一個最基礎的結構
但是問題來了!平時我們都是直接自己在腦子裡或者圖紙上先描述好這個樹,然後我們在對控制檯輸入我們想要的!
那麼我們如何能夠確定自己建立的一顆樹來是正確的呢?
有很多種辦法可以(這裡說兩種)
- 我們可以通過遍歷輸出我們所建立的樹形結構(先序,中序,後序,層次遍歷都可以)
優點 :程式碼思路簡單,很多書籍上直接給出。
缺點 :需多個方式(先序+中序或先序+後序)才能判別出
- 我們希望控制檯能夠以一種類似於我們在圖紙上的形式顯現出來。
優點 :直接顯現,錯誤一看便知
缺點 :程式碼思路較難,新手難以控制
樹的各種遍歷許多書籍已經給出,我的個人部落格上也有一些版本,網上的版本也大都一致。這裡不再敘述
下面介紹一種本人原創的一種思路:
首先這種想法需要一個另外的結構陣列來儲存你的樹節點
假如有一棵樹為下圖所示:
由上圖可知要想列印一顆比較直觀的二叉樹,我們最主要的就是要考慮那些理論完全二叉樹上沒有節點的地方如何控制輸出如上圖右邊的(5,7,8,9,11,12,13)節點上是空的這個時候我們應該用空格
或者其他自定義的字元來表示這個空節點
下面給出程式碼:
DisplayTree.c:
#include"BinaryTree.h" //按需選擇自己需要列印的各種型別樹; //但只支援類似下面樹結點的申明形式: //struct TreeNode //{ //TreeElementType Element; //struct TreeNode *Left; //struct TreeNode *Right; //}; #include<stdio.h> #include<stdlib.h> #include<math.h> /* 函式功能:直觀列印二叉樹 實現函式功能的封裝性; 本函式主要思想是給二叉樹結點做順序序號標記; 封裝完好,增強了功能性。 */ typedef BinTree Tree;//列印其他樹型別,請修改此處樹型別指標 struct FlagNode { TreeElementType Element; int Sign; }; typedef struct FlagNode *Flag; static int count = 0; Flag Initialize(int ArraySize); void MarkTreeNode(Tree T, Flag Array, int f); void PrintFlag(Flag Array, int ArraySize); void PrintBinaryTree(Flag Array, int ArraySize, int DepthOfTree); /*下列函式按需選擇*/ //int SumNode(Tree T); //int Depth(Tree T); void TreePrint(Tree T) { int i = 1;//從1號開始標記 int NumberOfTreeNode = SumNode(T); int DepthOfTree = Depth(T); //printf("DepthOfTree = %d\n", DepthOfTree); //printf("NumberOfTreeNode = %d\n", NumberOfTreeNode); Flag A; A = Initialize(NumberOfTreeNode); //這裡放一組深度為5的滿二叉樹元素組:phdba00c00fe00g00lji00k00nm00o00xtrq00s00vu00w00bzy00a00dc00e00 MarkTreeNode(T, A, i); //PrintFlag(A, NumberOfTreeNode); printf("The binary tree is look like:\n"); PrintBinaryTree(A, NumberOfTreeNode, DepthOfTree); } Flag Initialize(int ArraySize) { int i; Flag Array;//申請樹的元素個數 Array = (Flag)malloc(sizeof(struct FlagNode) * ArraySize); if(NULL == Array) { printf("Allocation failure!"); return NULL; } return Array; } //給樹結點做標記,按理論完全二叉樹的序號標記 void MarkTreeNode(Tree T, Flag Array, int f) { if(NULL != T) { Array[count].Sign = f; Array[count].Element = T->Element; count++; MarkTreeNode(T->Left, Array, f * 2); MarkTreeNode(T->Right, Array, f * 2 + 1); } } void PrintFlag(Flag Array, int ArraySize) { int i; for(i = 0; i < ArraySize; i++) printf("%c-->%d\n", Array[i].Element, Array[i].Sign); } void PrintBinaryTree(Flag Array, int ArraySize, int DepthOfTree) { int Line;//行數 int PerLineNode;//理論每行結點總數 int LineStart;//理論完全二叉樹的第一個結點序號 int Blank = DepthOfTree; for(Line = 1; Line <= DepthOfTree; Line++)//此迴圈控制列印的行數 { int i; /*下面得for迴圈控制輸出每行前面得空格*/ for(i = 0; i < pow(2, Blank - 1); i++) printf(" "); printf("\b"); PerLineNode = pow(2, Line - 1); /*理論上每行的第一個結點序號也是該行的理論總結點數(如下圖)*/ /*1*/ /*23*/ /*45 67*/ for(LineStart = PerLineNode; LineStart < PerLineNode * 2; LineStart++) { /*下面程式碼識別從LineStart從開始到末尾的序號是否存在結點,不存在則輸出" "*/ int exist = 0; for(i = 0; i < ArraySize; i++) { if(LineStart == Array[i].Sign) { printf("%c", Array[i].Element); exist = 1; } } if(exist == 0) printf(" "); /*下面for迴圈控制輸出每個理論存在結點得後得空格數*/ for(i = 0; i < pow(2, Blank); i++) printf(" "); printf("\b");//回退一格,具體細節可畫圖 } printf("\n"); Blank--; } } //下列兩個函式根據樹的操作可選 //int Depth(Tree T)//輸出的是整個二叉樹的深度 //{ //int DepthOfLeft = 0; //int DepthOfRight = 0; //if(NULL == T) //return 0; //else //{ //DepthOfLeft = Depth(T->Left); //DepthOfRight = Depth(T->Right); //return (DepthOfLeft > DepthOfRight) ? DepthOfLeft + 1 : DepthOfRight + 1; //} //} //int SumNode(Tree T) //{ //if(NULL == T) //return 0; //else if(T->Left == NULL && T->Right == NULL) //return 1; //else //return SumNode(T->Left) + SumNode(T->Right) + 1;//加1等於是每次返回 加一個根結點 //}
下面是我的一組簡單測試:
test.c:
#include"PrintTree.c" #include"TreeOperate.c" #include<stdio.h> #include<stdlib.h> int main() { BinTree BT; BT = IterationCreateTree(); TreePrint(BT); return 0; }
輸出圖樣: