---------------------------------------------2018.10.25修改------------------------------------------------------------------
重構部分代碼,加入了打印樹枝的函數,但是還不夠完美!!先暫時放下
-------------------------------------------------------------------------------------------------------------------------------------------------------
當我們學習樹這種數據結構時會牽扯到很多的東西,基本上學習數據結構的一大重心都圍繞着樹這一個最基礎的結構
但是問題來了!平時我們都是直接自己在腦子里或者圖紙上先描述好這個樹,然后我們在對控制台輸入我們想要的!
那么我們如何能夠確定自己創建的一顆樹來是正確的呢?
有很多種辦法可以(這里說兩種)
- 我們可以通過遍歷輸出我們所創建的樹形結構(先序,中序,后序,層次遍歷都可以)
優點:代碼思路簡單,很多書籍上直接給出。
缺點:需多個方式(先序+中序或先序+后序)才能判別出
- 我們希望控制台能夠以一種類似於我們在圖紙上的形式顯現出來。
優點:直接顯現,錯誤一看便知
缺點:代碼思路較難,新手難以控制
樹的各種遍歷許多書籍已經給出,我的個人博客上也有一些版本,網上的版本也大都一致。這里不再敘述
下面介紹一種本人原創的一種思路:
首先這種想法需要一個另外的結構數組來保存你的樹節點
假如有一棵樹為下圖所示:
由上圖可知要想打印一顆比較直觀的二叉樹,我們最主要的就是要考慮那些理論完全二叉樹上沒有節點的地方如何控制輸出如上圖右邊的(5,7,8,9,11,12,13)節點上是空的這個時候我們應該用空格
或者其他自定義的字符來表示這個空節點
下面給出代碼:
DisplayTree.h:
#ifndef _PRINTTREE_H #define _PRINTTREE_H #include "BinaryTree.h" //按需選擇自己需要打印的各種類型樹; //但只支持類似下面樹結點的申明形式: //struct TreeNode //{ // TreeElementType Element; // struct TreeNode *Left; // struct TreeNode *Right; //}; typedef BinaryTree Tree; //打印其他樹類型,請修改此處樹類型指針 struct FlagNode { ElementType Element; int SerialNumber; }; typedef struct FlagNode *Flag; void DisplayTree(Tree T); void MarkTreeNode(Tree T, Flag Array, int f); void PrintFlag(Flag Array, int ArraySize); void Display(Flag Array, int ArraySize, int DepthOfTree); void OutSpace(int n); void OutBranch(int haveLeft, int haveRight, int level); int AllNodes(Tree T); int TotalDepth(Tree T); #endif
DisplayTree.c:
#include "DisplayTree.h" #include <math.h> #include <stdio.h> #include <stdlib.h> //這里放一組深度為5的滿二叉樹元素組: // phdba00c00fe00g00lji00k00nm00o00xtrq00s00vu00w00bzy00a00dc00e00 static int count = 0; void DisplayTree(Tree T) { int allNodes = AllNodes(T); int depth = TotalDepth(T); Flag A = (Flag)malloc(sizeof(struct FlagNode) * allNodes); if (NULL == A) return; MarkTreeNode(T, A, 1); //PrintFlag(A, allNodes); printf("The binary tree is look like:\n"); Display(A, allNodes, depth); } //給樹結點做標記,按理論完全二叉樹的序號標記 void MarkTreeNode(Tree T, Flag Array, int No) { if (NULL != T) { Array[count].SerialNumber = No; Array[count++].Element = T->Element; MarkTreeNode(T->Left, Array, No * 2); MarkTreeNode(T->Right, Array, No * 2 + 1); } } void Display(Flag Array, int ArraySize, int depth) { int lineStart, lineEnd; int spaceInFront = depth - 1; int interval = depth;//節點之間的間隔指數 for (int level = 0; level < depth; level++) { /* level:0 ___1 */ /* level:1 _2___3 */ /* level:2 4_5_6_7 */ lineStart = pow(2, level); lineEnd = lineStart * 2; OutSpace(spaceInFront); //輸出每行前面的空格 for (int start = lineStart; start < lineEnd; start++) { /*eg:從4,5,6,7按順序檢測該標志是否存在,存在輸出節點,不存在則輸出"0"*/ int exist = 0; for (int i = 0; i < ArraySize; i++) { if (start == Array[i].SerialNumber) { printf("%c", Array[i].Element); exist = 1; } } if (exist == 0) printf(" "); OutSpace(interval); //輸出一個節點后的空格 } printf("\n"); spaceInFront--; interval--; OutSpace(spaceInFront); for (int parent = lineStart; parent < lineEnd; parent++) { int exist = 0; for (int i = 0; i < ArraySize; i++) { if (parent == Array[i].SerialNumber) { int haveLeft = 0, haveRight = 0; //記錄當前節點是否有左右孩子 0 為無 int leftChild = parent * 2; int rightChild = parent * 2 + 1; for (int last = i; last < ArraySize; last++) { if (Array[last].SerialNumber == leftChild) haveLeft = 1; if (Array[last].SerialNumber == rightChild) haveRight = 1; } OutBranch(haveLeft, haveRight, interval); OutSpace(interval); exist = 1; } } if (!exist) { OutBranch(0, 0, interval); OutSpace(interval); } } printf("\n"); } } /*可變長樹枝"┌─────┴─────┐"*/ void OutBranch(int haveLeft, int haveRight, int interval) { if (haveLeft) { printf("┌"); for (int i = 0; i < pow(2, interval) / 2 - 1; i++) printf("─"); if (haveRight) { /*"┌─────┴─────┐"*/ printf("┴"); for (int i = 0; i < pow(2, interval) / 2 - 1; i++) printf("─"); printf("┐"); } else { /*"┌─────┘ "*/ printf("┘"); for (int i = 0; i < pow(2, interval) / 2 - 1; i++) printf(" "); printf(" "); } } else { printf(" "); for (int i = 0; i < pow(2, interval) / 2 - 1; i++) printf(" "); if(haveRight) { /*" └──────┐"*/ printf("└"); for (int i = 0; i < pow(2, interval) / 2 - 1; i++) printf("─"); printf("┐"); } else { /*" "*/ printf(" "); for (int i = 0; i < pow(2, interval) / 2 - 1; i++) printf(" "); printf(" "); } } } void OutSpace(int n) { for (int i = 0; i < pow(2, n); i++) printf(" "); printf("\b"); } //輔助debug函數 void PrintFlag(Flag Array, int ArraySize) { for (int i = 0; i < ArraySize; i++) printf("%c-->%d\n", Array[i].Element, Array[i].SerialNumber); } int TotalDepth(Tree T) //輸出的是整個二叉樹的深度 { int DepthOfLeft = 0; int DepthOfRight = 0; if (NULL == T) return 0; else { DepthOfLeft = TotalDepth(T->Left); DepthOfRight = TotalDepth(T->Right); return (DepthOfLeft > DepthOfRight) ? DepthOfLeft + 1 : DepthOfRight + 1; } } int AllNodes(Tree T) { if (NULL == T) return 0; else if (T->Left == NULL && T->Right == NULL) return 1; else return AllNodes(T->Left) + AllNodes(T->Right) + 1; //加1等於是每次返回 加一個根結點 }
下面是我的一組簡單測試:(注意此處我創建的樹元素是我已經寫好了CreateTree()直接用的,具體看你自己用的樹操作)
test.c:
#include"DisplayTree.h" #include"BinaryTree.h" #include<stdio.h> #include<stdlib.h> int main() { BinTree BT; BT = CreateTree(); DisplayTree(BT); return 0; }
輸出圖樣: