寫這個程序的目的是學習數據結構的時候方便調試,學習起來也比較直觀。
這個是我測試SplayTree時候的gif
STEP 1
新建一個頭文件,命名為DrawATree.hh, 將以下內容復制進去
#ifndef DRAWTREE_HH
#define DRAWTREE_HH
#include <ostream>
#include <sstream>
#include <iostream>
#include <cmath>
#include <algorithm>
namespace
{
#define lChild l_child_ //使用前將 l_child_ 更換為 自己樹節點的左孩子的名字
#define rChild r_child_ //使用前將 r_child_ 更換為 自己樹節點的右孩子的名字
#define data data_ //使用前將 data_ 更換為 自己樹節點的數據的變量名
#define MAXN (1000) //這棵樹的節點上限
#define PERID (2) //打印兩個節點的橫坐標間隔
unsigned int SUM; //統計當前遍歷到第幾個節點
#ifdef _WIN32
void clear() {system("cls"); }
#elif __linux__
void clear() { system("clear"); }
#endif
// 將光標移動到 (X,Y)
std::string AXIS(int X, int Y) {
std::stringstream ss;
ss<< "\033["<< Y << ";" <<X<<"H";
return ss.str();
}
struct DrawNode{
int x,y,dataSize;
}axisArray[MAXN];
//計算節點數據輸出的長度
template <typename TreePtr>
int dataSize(TreePtr const& root) {
std::stringstream ss;
ss << (root->data);
return (ss.str()).length();
}
//中序遍歷, 從左往右畫節點(不連線)
//橫坐標通過全局變量SUM和上一個節點數據的輸出長度算出
//縱坐標通過遞歸深度判斷
//PERID 是兩個節點間隔長度
template <typename TreePtr>
void buildDrawTree (TreePtr const& root, int deep) {
if(!root) return; //判斷空節點,如果你的節點判空和我不一樣,這里也要改, 比如我之前的判斷空節點是(root->height_== -1).
if(root->lChild) buildDrawTree(root->lChild, deep+1);
axisArray[SUM] = (struct DrawNode){axisArray[SUM-1].x+axisArray[SUM-1].dataSize+PERID, deep, dataSize(root)};
std::cout << AXIS(axisArray[SUM].x, axisArray[SUM].y) << root->data;
++SUM;
if(root->rChild) buildDrawTree(root->rChild, deep+1);
}
template <typename TreePtr>
void Draw (TreePtr const& t) { //畫樹函數
clear(); //清屏
SUM = 1;
int maxy = 0;
buildDrawTree<TreePtr> (t, 2); //每個結點畫出來
//畫節點間連線,因為畫的節點不會太多,所以就寫了n^2的算法,比較好實現
//每個節點只有一個父節點,所以畫出每個節點和自己父節點的連線即可
for(int i=1; i<SUM; i++) {
//x,y是子節點的坐標,p是父節點的axisArray數組的下標, px,py是父節點的坐標;
int x = axisArray[i].x, y = axisArray[i].y, p=0, px=0, py=y-1;
if(y==1) continue; // 根結點沒有父節點,跳過
for(int j=1; j<SUM; j++) { //循環找父節點
if(i==j) continue;
if((!p || abs(axisArray[j].x-x) < abs(px-x)) && axisArray[j].y+1 == y)
p = j, px = axisArray[j].x;
}
int s = (2*x+axisArray[i].dataSize)>>1;
std::cout << AXIS(s,py) << '+';
if(s<px)
for(int i=s+1; i<px; i++) std::cout << AXIS(i,py) << '-';
else
for(int i=px+axisArray[p].dataSize;i<s; i++) std::cout << AXIS(i,py) << '-';
maxy = std::max(maxy, y);
}
std::cout << AXIS(1,maxy+1); //打印完把光標移到最下邊.
// getchar();
}
} // namespace
#endif
STEP 2
修改頭文件DrawATree.hh 第12,13,14,51
行代碼,如果需要的話. 代碼中有注釋說明怎么修改.
STEP 3
測試你的源代碼.
要保證你的節點數據可以用cout<<輸出
#include "DrawATree.hh"
#include "YourTree.hh"
int main() {
YourTreeType t;
Draw(t.root()); // 這里要傳入你的根結點的指針!
}
實例:
樹的頭文件: Tree.hh
typedef struct TreeNode Node;
typedef Node* PtrNode;
struct TreeNode{
char c_;
PtrNode l_child_, r_child_;
TreeNode(char c) : c_(c), l_child_(0), r_child_(0) {}
void Insert(PtrNode p, bool left);
};
void Node::Insert(PtrNode p, bool left) {
if(left) l_child_ = p;
else r_child_ = p;
}
源代碼:
#include "Tree.hh"
#include "DrawATree.hh"
#define left (1)
#define right (0)
int main() {
PtrNode root = new TreeNode('a');
root->Insert(new TreeNode('b'), left);
root->Insert(new TreeNode('c'), right);
Draw(root);
getchar();
}
效果: