1. 程式人生 > >動態展示二叉搜尋樹之實踐進行時

動態展示二叉搜尋樹之實踐進行時

目錄

一、選題展示

二、思考題目

三、階段一:圖形顯示二叉搜尋樹之插入

1.圖形顯示二叉搜尋樹的要求

2.原博主的程式碼和輸出

四、階段二:圖形顯示二叉搜尋樹之查刪

1.二叉搜尋樹的查詢操作

2.二叉搜尋樹的刪除操作

五、階段三:動態顯示二叉搜尋樹

六、階段四:加入調整規則使二叉搜尋樹變為平衡樹

七、階段五:動態顯示二叉平衡樹

八、階段六:使用多種方式顯示二叉平衡樹

1.java 2D  

2.呼叫graphviz繪製。

3.java+neo4j

九、階段七:圖形顯示B-樹

十、階段八:動態顯示B-樹




一、選題展示

B5:搜尋樹操作程式

開發建立搜尋樹的程式,在程式中可以進行引數設定、資料元素輸入與操作選擇,支援搜尋樹結構中的各種資料操作(如插入、刪除、搜尋等),可使用C、C++或Java等程式語言實現。

基本要求

(1) 連續輸入若干資料元素,程式自動在畫板上畫出相應二叉搜尋樹;可以對已經生成的二叉搜尋樹進行插入、刪除和搜尋操作,程式動態顯示操作結果。

(2) 連續輸入若干資料元素,在畫板上畫出相應二叉平衡樹;可以對已經生成的二叉平衡樹進行插入、刪除和搜尋操作,程式動態顯示操作結果。

(3) 實物演示時要求演示結果正確。

(4) 程式操作友好、健壯。

提高要求:

(1) 連續輸入若干資料元素,程式能動態畫出相應B-樹。

(2) 可以對已經生成的B-樹進行插入、刪除和搜尋操作,程式動態顯示操作結果。

(3) 圖形化介面,樹形美觀對稱。

二、思考題目

個人對“動態”的理解

 模仿終端的形式,在c語言的輸入框裡,通過“add XX”、“delete XX”、“find XX”進行插入、刪除和搜尋操作,每次輸入後,都會輸出二叉平衡樹/B-樹,輸入“quit”退出程式。

根據題目要求,將整個專案劃分為以下階段:

階段一:圖形顯示二叉搜尋樹之插入

階段二:圖形顯示二叉搜尋樹之查刪

階段三:動態顯示二叉搜尋樹

階段四:加入調整規則使二叉搜尋樹變為平衡樹

階段五:動態顯示二叉平衡樹

階段六:使用多種方式顯示二叉平衡樹

階段七:圖形顯示B-樹

階段八:動態顯示B-樹

圖形展示參考:二叉樹的圖形顯示

三、階段一:圖形顯示二叉搜尋樹之插入

1.圖形顯示二叉搜尋樹的要求

  • 層次化輸出:同層次遍歷,但是需要在每層輸出完畢後進行換行。可由層數和孩子節點的關係2^(n-1)類推出所有層的節點總數,待輸入完畢後,進行自動按層換行。

  • 確定相對位置:安排節點位置用以直觀的得到節點之間的關係,使用如下思路:在節點型別中增加成員變數int pos,用以表示節點所在位置。如果父節點為P,位置為P.pos,則可在下一層中,令左孩子L的位置滿足如下關係L.pos=P.pos-1,右孩子R的位置滿足R.pos=P.pos+1,是根節點位置為一個大小適宜的正數M,則可避免子樹中節點位置出現負數的情況。

  • 輸出衝突:不同雙親節點的子節點可能出現位置重疊的情況:

    對已一個節點P,定義它的左子樹中的結點pos最大值設為LMax(P),右子樹中節點pos最小值為RMin(P)。 輸出一棵樹時不會有衝突的充分條件是對於每一個結點P都有:LMax(P) < P.pos < RMin(P),也就是所有左子樹中的結點都在根節點的左方,所有右子樹中的結點都在根結點的右方。

    如果某一結點不滿足LMax(P)<P.pos,就將P和右子樹中的左右結點都右移LMax(P) - P.max + 1個位置。如果不滿足P.pos < RMin(P),就將右子樹右移RMin(P)-P.pos + 1個位置。具體的處理過程是這樣的:按照層次遍歷的順序,自上到下、自左向右地檢查每個結點是否和左、右子樹存在衝突,如果存在,就移動P和右子樹。

    移動之後有兩種情況,一種是結點P是其父節點(PP)的左孩子,這是P和P的右子樹都屬於PP的左子樹,移動P和P的右子樹有可能會造成條件LMax(PP) < P.pos的失敗,所以這個時候需要對PP再進行一次衝突判斷,這樣遞歸向上知道沒有衝突。第二種情況是P是PP的右孩子,這種情況下移動P和右子樹肯定不會造成PP的衝突。在P的祖父節點中,設第一個具有左孩子的節點為A,移動P和P的右子樹有可能造成A的衝突,需要再對A進行衝突處理。
     

2.程式碼和輸出

InfoH.h

用於定義二叉樹節點所要繼承的類,如上所說,定義了表示位置的成員變數pos。

 struct InfoH {
    int pos;
    bool newline;
 
    InfoH() : pos(INFI), newline(false) {}
 
    int add_pos(int add) {
        pos += add;
        return pos;
    }
};

PosSetter.h

引用InfoH.h檔案,初始化二叉樹中每個節點的成員變數pos,在函式VisualTree::draw()中被呼叫。

#include "InfoH.h"
 
template<class TreeNode>
class PosSetter 
{
public:
    PosSetter(TreeNode* TreeNode::* p, TreeNode *TreeNode::* l, TreeNode *TreeNode::* r) :
        parent(p), left(l), right(r) 
    {
    }
    
    void operator() (TreeNode *node) 
	{
        TreeNode *p = node->*parent;
        if (p != NULL) {
            if (node == p->*left)  { node->pos = p->pos - 1; }
            if (node == p->*right) { node->pos = p->pos + 1; }
        }
    }
 
private:
    TreeNode *TreeNode:: *parent;
    TreeNode *TreeNode:: *left;
    TreeNode *TreeNode:: *right;
};

PosAdder.h

用以調整節點的pos值,調整值在例項化物件的時候進行指定,每個物件對應一個值。

#include "InfoH.h"
 
class PosAdder
{
public:
    PosAdder(int n_) : n(n_) {}
    int operator() (InfoH *node) { return node->add_pos(n); }
private:
    int n;
};

ExtremumGetter.h

遍歷結點的時候, 記錄下節點中的極值。

template<class TreeNode>
class ExtremumGetter
{
public: 
    ExtremumGetter(TreeNode *min = 0, TreeNode *max = 0) {
        init(min, max);
    }
 
    void operator() (TreeNode *p) {
        if ((min_ && p->pos < min_->pos) || !min_) { min_ = p; }
        if ((max_ && p->pos > max_->pos) || !max_) { max_ = p; }
    }
 
    void init(TreeNode *min, TreeNode *max) { 
        min_ = min; max_ = max;
        if (min_ && max_ && max_->pos < min_->pos) {
            std::swap(min_, max_);
        }
    }
 
    TreeNode *min() const { return min_; }
    TreeNode *max() const { return max_; }
 
private:
    TreeNode *min_;
    TreeNode *max_;
};
 

以上三個檔案中定義的三個類都過載了operator (),他們都是在呼叫VisualTree::traverse_level()的時候最為函式物件被呼叫的。

VisualTree.h

#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <queue>
#include "ExtremumGetter.h"
#include "PosAdder.h"
#include "PosSetter.h"
#include "TreePrinter.h"
 
struct IntExtremumPair {
    int min;
    int max;
    IntExtremumPair(int rh1 = 0, int rh2 = 0) : min(rh1), max(rh2) {}
};
 
template<class TreeNode, class ValueType>
class VisualTree 
{
public:
    typedef TreeNode *TreeNode::*           PtrToMember;
    typedef ValueType TreeNode::*           PtrToData;
 
    VisualTree(PtrToMember p, PtrToMember lc, PtrToMember rc, PtrToData val);
    
    void draw(TreeNode *node, const char *promot = NULL);
 
private:
    void                     adjust_pos(TreeNode *root);
    TreeNode*                ancestor(TreeNode *node);
    int                      digits(int n);
    int                      digits(char c);
    int                      digits(const char*s);
    IntExtremumPair          extreme_pos(TreeNode *p);
    ExtremumGetter<TreeNode> extreme_node(TreeNode *root);
    int                      scan_tree(TreeNode *root); 
    template<class VST> void traverse_level(TreeNode *p, VST &vst); 
 
 
    ExtremumGetter<TreeNode> getter;
    TreePrinter<TreeNode, ValueType> printer;
    PosSetter<TreeNode>      setter;
    PtrToMember              parent, left, right;
    ValueType TreeNode::    *value;
};
 
/*
 * 建構函式
 */
template<class TreeNode, class ValueType>
VisualTree<TreeNode, ValueType>::VisualTree(PtrToMember p, PtrToMember lc, PtrToMember rc, PtrToData val) :
    getter(), printer(p,lc,rc,val), setter(p,lc,rc), parent(p), left(lc), right(rc), value(val)
{
}
 
/*
 * 越界調整    
 */
template<class TreeNode, class ValueType>
void VisualTree<TreeNode, ValueType>::adjust_pos(TreeNode *root)
{
    if (root == NULL) {
        return;
    }
 
    int diff = 0;
    IntExtremumPair extr;
    if (root->*left) {
        extr = extreme_pos(root->*left);
        if (root->pos <= extr.max) { // 左子樹越界:將根節點右移及其右子樹右移
            diff = extr.max - root->pos + 1; 
            PosAdder adder(diff);
            root->add_pos(diff);
            traverse_level(root->*right, adder);
            adjust_pos(ancestor(root));
        }
    } 
 
    if (root->*right) { // 右子樹越界:將右字樹右移
        extr = extreme_pos(root->*right);
        if (extr.min <= root->pos) {
            diff = root->pos - extr.min + 1;
            PosAdder adder(diff);
            traverse_level(root->*right, adder);
            adjust_pos(ancestor(root));
        }
    }
}
 
/*
 * 尋找節點node的第一個具有左孩子的祖先
 * @return 祖先地址或者NULL
 */
template<class TreeNode, class ValueType>
TreeNode *VisualTree<TreeNode, ValueType>::ancestor(TreeNode *node)
{
    TreeNode *pchild = node;
    TreeNode *pparent = node->*parent;
    while (pparent && pchild == pparent->*right) {
        pchild = pparent;
        pparent = pchild->*parent;
    }
 
    return pparent;
}
 
/*
 * 計算一個整數n輸出時佔用的字元數
 * @return 整數n輸出時所佔字元數
 */
template<class TreeNode, class ValueType>
int VisualTree<TreeNode, ValueType>::digits(int n) 
{
    int len = 0;
    if (n < 0) { 
        len++; 
        n = -n; 
    }
    do {
        len++;
        n /= 10;
    } while (n);
    return len;
}
 
/*
 * 輸出一個字元所佔用的字元數
 * @return 1
 */
template<class TreeNode, class ValueType>
int VisualTree<TreeNode, ValueType>::digits(char c) 
{
    return 1;
}
 
 
/*
 * 輸出一個字串所佔用的字元數
 * @return 字串長度
 */
template<class TreeNode, class ValueType>
int VisualTree<TreeNode, ValueType>::digits(const char *s)
{
    return strlen(s);   
}
 
/*
 * 圖形化顯示二叉樹
 * @input root:樹的根節點,promot:可選提示資訊,應以'\0'結尾
 */
template<class TreeNode, class ValueType>
void VisualTree<TreeNode, ValueType>::draw(TreeNode *root, const char *promot)
{   
    if (promot) { 
        printf("%s\n", promot);
    } 
 
    if (root == NULL) {
        printf("Empty tree!\n");
        return;
    }
 
    int len = scan_tree(root);
 
    printer.set_edge_length(len);
    traverse_level(root, printer);  
}
 
/*
 * 獲得最靠左和最靠右的兩個節點指標
 * @return std::pair, 其中first指向最左節點,second指向最右節點
 */
template<class TreeNode, class ValueType>
ExtremumGetter<TreeNode> 
VisualTree<TreeNode, ValueType>::extreme_node(TreeNode *node)
{
    getter.init(node, node);
    traverse_level(node, getter);
    return getter;
}
 
/* 
 * 獲得最靠左的節點和最靠右的節點pos
 * @return 包含最小/最大位置座標的IntExtremumPair
 */
template<class TreeNode, class ValueType>
IntExtremumPair VisualTree<TreeNode, ValueType>::extreme_pos(TreeNode *node)
{
    ExtremumGetter<TreeNode> nodes = extreme_node(node);
    IntExtremumPair ret;
    if (nodes.min()) { ret.min = nodes.min()->pos; }
    if (nodes.max()) { ret.max = nodes.max()->pos; }
    return ret;
}
 
/*
 * 掃描整棵樹,設定相關資訊(換行標記/資料位數)
 * @return 最大資料位數
 */
template<class TreeNode, class ValueType>
int VisualTree<TreeNode, ValueType>::scan_tree(TreeNode *root)
{
    int                    cnt;     // 當前深度已掃描節點個數
    int                    depth;   // 當前掃描深度
    int                    max_len; 
    std::queue<TreeNode*>  qnode;
    std::vector<int>       num_node;  // num_node[i]:  深度為i的節點總數
    
    num_node.push_back(1); // 一個根節點
    num_node.push_back(0); // 初始化第1層
    qnode.push(root);
 
    // 將根節點位置設為0,據此算出其他節點相對位置 
    root->pos = 0; 
    traverse_level(root, setter);
 
    // 獲取最左最右座標
    IntExtremumPair extr = extreme_pos(root);
 
    // 將最左節點座標調整為0,其他節點整體右移
    PosAdder adder(root->pos - extr.min);
    traverse_level(root, adder);
 
    cnt = 0; depth = 0; max_len = 0;
    for ( ; !qnode.empty(); qnode.pop()) {
        TreeNode *temp = qnode.front();
        adjust_pos(temp);
        if (temp->*left)  { 
            qnode.push(temp->*left);
            num_node[depth+1]++;
        }
        if (temp->*right) { 
            qnode.push(temp->*right); 
            num_node[depth+1]++;
        }
        
        if (++cnt == num_node[depth]) {
            temp->newline = true;
            depth++;
            num_node.push_back(cnt = 0); // 初始化下下層節點個數
        } else {
            temp->newline = false;
        }
 
        max_len = std::max(max_len, digits(temp->*value));
    }
 
    // 使樹和螢幕左側之間不留空隙
    extr = extreme_pos(root);
    if (extr.min > 0) { 
        PosAdder adder(-extr.min);
        traverse_level(root,adder);
    }
    return max_len;
}
 
/* 
 * 層次遍歷二叉樹,對樹中每個節點執行操作vst
 */
template<class TreeNode, class ValueType>
template<class VST>
void VisualTree<TreeNode, ValueType>::traverse_level(TreeNode *root, VST &vst)
{
    if (root == NULL) {
        return;
    }
 
    TreeNode             *temp;
    std::queue<TreeNode*> qnode;
    for (qnode.push(root); !qnode.empty(); qnode.pop()) {
        temp = qnode.front();
        vst(temp);
        if (temp->*left)  { qnode.push(temp->*left); }
        if (temp->*right) { qnode.push(temp->*right); }
    }
}

TreePrinter.h

template<class TreeNode, class ValueType>
class TreePrinter
{
public:
    typedef TreeNode *TreeNode::*PtrToMember;
    typedef ValueType TreeNode::*PtrToData;
 
    TreePrinter(PtrToMember p, PtrToMember l, PtrToMember r, PtrToData d) : 
        edge_len(2), num_out(0), vec(), parent(p), left(l), right(r), value(d)
    {
    }
 
    void set_edge_length(int len) 
    {
        edge_len = std::max(len, 2); // 邊沿最小寬度為2
    }
 
    void operator() (TreeNode *node) 
    {
        assert(node);
        TreeNode *lc = node->*left;    
        TreeNode *rc = node->*right;   
        int      lbl = 0, rbl = 0;  // 左邊沿字元長度,右邊沿字元長度 
        int      spaces = node->pos * edge_len - num_out; // 佔位空白字元數
 
        if (lc) { lbl = edge_len * (node->pos-(node->*left)->pos) - 1; }
        if (rc) { rbl = edge_len * ((node->*right)->pos-node->pos) - 1; }
        
        spaces -= lbl;
        assert(spaces >= 0);
 
        while (spaces--) { 
            num_out += printf(" "); 
        }
        
        if (node->*left) { 
            vec.push_back(num_out-1);
            while (lbl--) {  
                num_out += printf("_");
            }
        }
        
        num_out += out_value(node->*value);
                
        if (node->*right) {
            while (rbl--) {
                num_out += printf("_"); 
            }
            vec.push_back(num_out); 
        }
 
        if (node->newline) { 
            new_line();
        }
    }
 
private:
    int out_value(char c)        { return printf("%c", c); }
    int out_value(int i)         { return printf("%d", i); }
    int out_value(const char *p) { return printf("%s", p); }
 
    void new_line() 
    {
         printf("\n");
         if (!vec.empty()) {
             int n = 0, end = vec[vec.size()-1];
             for (int i = 0; i <= end && n < (int)vec.size(); ++i) {
                 if (i == vec[n]) {
                     printf("|");
                     n++;
                 } else {
                     printf(" ");
                 }
             }
         }
         printf("\n");
         num_out = 0;
         vec.clear();
    }
 
    int                   edge_len;
    int                   num_out;   // 已輸出字元數
    std::vector<int>      vec;
    PtrToMember           parent;
    PtrToMember           left;
    PtrToMember           right;
    ValueType TreeNode:: *value;
};
 

bst.h    為了方便測試寫的一個Binary Search Tree,在main.cc中想BST中隨機插入一定數量的結點,然後顯示:

#include <cassert>  
#include <algorithm> // swap
#include "InfoH.h"
 
#define IS_ROOT(p)   ((p)->parent == NULL)
#define IS_LEAT(p)   ((p)->left == NULL && (p)->right == NULL)
#define IS_LEFT(p)   (!IS_ROOT(p) && (p) == (p)->parent->left)
#define IS_RIGHT(p)  (!IS_ROOT(p) && (p) == (p)->parent->right)
 
template<typename T>
struct TreeNode : public InfoH {
    T        val;
    TreeNode *parent;
    TreeNode *left;
    TreeNode *right;
 
    TreeNode(const T &v) : InfoH(), val(v), parent(0), left(0), right(0) {}
    TreeNode(const T &v, TreeNode *p) : InfoH(), val(v), parent(p), left(0), right(0) {}
};
 
template<typename T>
class BSTree 
{
public:
    /*
     * 建構函式
     */
    BSTree() : root_(0) {}
 
    /*
     * 解構函式
     */
    virtual ~BSTree() 
    {
        while (!empty()) {
            remove(root_);
        }
    }
 
    /*
     * 判斷樹是否為空
     * @return true:空, false:非空
     */
    bool empty() 
    {
        return root_ == NULL;   
    }
 
    /* 
     * 插入一個新節點
     * @return 新插入節點地址
     */
    TreeNode<T> *insert(const T &val) 
    {
        if (root_ == NULL) {
            return root_ = new TreeNode<T>(val);
        }
 
        TreeNode<T> *parent = root_;
        TreeNode<T> *hole = 0;
        
        hole = (root_->val < val ? root_->right : root_->left);
        while (hole != NULL) {
            parent = hole;
            hole = (hole->val < val ? hole->right : hole->left);
        }
 
        if (parent->val < val) {
            return parent->right = new TreeNode<T>(val, parent);
        } else {
            return parent->left = new TreeNode<T>(val, parent);
        }
    }
 
    /* 
     * 尋找值為x的節點
     * @reutrn 節點地址,如果不存在返回NULL
     */
    TreeNode<T> *find(T &x) 
    {
        TreeNode<T> *temp = root_;
        while (temp != NULL) {
            if (temp->val == x) { 
                break;
            }
            temp = (temp->val < x ? temp->right : temp->left);
        }
        // temp == NULL
        return temp;
    }
 
    /* 
     * 刪除指定節點
     */
    void remove(TreeNode<T> *p) 
    {
        assert(p != NULL);
 
        TreeNode<T> *temp = NULL;
        if (p->left && p->right) {          // 有兩個孩子
            TreeNode<T> *temp = succ(p); 
            std::swap(p->val, temp->val);   // 交換和後繼節點的內容
            remove(temp);                   // 刪除後繼節點
        } else if (p->left || p->right) {   // 只有一個孩子
            temp = (p->left ? p->left : p->right); 
            if (IS_ROOT(p)) {
                root_ = temp;
                root_->parent = NULL;
            } else if (IS_LEFT(p)) {
                p->parent->left = temp;
                temp->parent = p->parent;
            } else {
                p->parent->right = temp;
                temp->parent = p->parent;
            }
            delete p;
        } else { // 沒有孩子
            if (IS_ROOT(p)) {
                root_ = NULL;
            } else if (IS_LEFT(p)) {
                p->parent->left = NULL;
            } else {
                p->parent->right = NULL;
            }
            delete p;
        }
    }   
 
    /*
     * 返回根節點地址
     */
    TreeNode<T> *root() const
    {
        return root_;
    }
private:
    /* 
     * 尋找最小節點
     * @reutrn 最小節點地址
     */
    TreeNode<T> *min_node(TreeNode<T> *p) 
    {
        assert(p != NULL);
        TreeNode<T> *temp = p;
        while (temp->left != NULL) {
            temp = temp->left;
        }
        return temp;
    }
 
    /* 
     * 尋找最大節點
     * @return 最大節點地址
     */
    TreeNode<T> *max_node(TreeNode<T> *p) 
    {
        assert(p != NULL);
        TreeNode<T> *temp = p;
        while (temp->right != NULL) {
            temp = temp->right;
        }
        return temp;
    }
 
    /* 
     * 尋找指定節點p的後繼節點(比p節點大的所有節點中的最小節點)
     * @return 後繼節點地址
     */
    TreeNode<T> *succ(TreeNode<T> *p) 
    {
        assert(p != NULL);
        if (p->right) {    // 節點有右子樹:右子樹中最小節點即為後繼節點
            return min_node(p->right);
        }
        // else 
        TreeNode<T> *child = p;
        TreeNode<T> *father = p->parent;
        while (father && !IS_LEFT(child)) { // 節點無右子樹:第一個有左孩子的祖先節點即為後繼節點
            child = father;
            father = child->parent;
        }
        return father;
    }
 
    TreeNode<T> *root_;
};

main.c

#include <iostream>
#include <vector>
#include "bst.h"
#include "VisualTree.h"
 
 
int main(int argc, char*argv[])
{
    typedef int                 ValueType;
    typedef TreeNode<ValueType> NodeType;
 
    BSTree<ValueType>                tree;
    VisualTree<NodeType, ValueType>  vtree(&NodeType::parent, 
                                            &NodeType::left, 
                                            &NodeType::right,
                                            &NodeType::val);
 
    int num = 15;
 
    srand(time(NULL));
    for (int i = 0; i < num; ++i) {
        ValueType v = rand() % 1000;  // 隨機生成一個[0,1000)的數
        tree.insert(v);
        std::cout << "insert : " << v << std::endl;
        vtree.draw(tree.root());
    }
 
    return 0;   
}

對博文的引用即到這裡,下面就是自己的思考和探索了。這位博主是很666的,想到當年輸出個*組成的大紅心就費了好大勁,不得不給他點個贊啦哈哈。

程式執行結果:

                            

四、階段二:圖形顯示二叉搜尋樹之查刪

1.二叉搜尋樹的查詢操作

根據二叉搜尋樹的特點,將待查詢的值和根節點進行比較,小於根節點就前往左子樹查詢,大於根節點前往右子樹查詢,等於根節點即為查詢結果。返回的結果"已找到",如果不存在返回“未找到”

可在main.c進行輸出值的判斷:

ValueType search=99;
    if(!tree.find(search)){
        printf("未找到\n");
    }

2.二叉搜尋樹的刪除操作

在查詢基礎上進行刪除,最後再次顯示:

五、階段三:動態顯示二叉搜尋樹

就我個人的理解來說,動態就是,像命令列一樣,輸入指令進行動態的增刪改查,並輸出結果。

在main.c函式中進行修改:

  int t=100;//預設最多操作100次
    int m=0,n=0;
    while(t){
        printf("-----------------------------------------------\n");
        printf("--------------------請輸入指令-------------------\n");
        printf("---      插入:1,num             查詢:2,num    ---\n");
        printf("---      刪除:3,num             退出:4        ---\n");
        printf("---                     (注:num設定為0~100) ---\n");
        printf("-----------------------------------------------\n");
        scanf("%d,%d",&m,&n);
        printf("\n-----------------------------------------------\n");
        //健壯性1
        if(m!=1&&m!=2&&m!=3&&m!=4){
            printf("輸入指令錯誤,請重新輸入。\n");
            continue;
        }
        //健壯性2
        if(n<0||n>100){
            printf("輸入數字錯誤,請重新輸入。\n");
            continue;
        }
        if(m==1){
            ValueType v =n;
            tree.insert(v);
            std::cout << "insert : " << v << std::endl;
            vtree.draw(tree.root());
        }else if (m==2){
            ValueType search =n;
            if(!tree.find(search)){
                        printf("未找到\n");
                    }else{
                        printf("已找到\n");
                    }
        }else if(m==3){
            ValueType search=n;
            if(!tree.find(search)){
                printf("未找到\n");
            }else{
                printf("已找到,並刪除\n");
            }
            TreeNode<int> *p=tree.find(search);
            tree.remove(p);
            vtree.draw(tree.root());
        }else if(m==4){
            break;
        }
        t--;
    }
    return 0;

執行效果:

                                     

錯誤輸入:

                                   

異常記錄:

  • 有時插入數字時會出現不斷執行迴圈的情況,儘管這種錯誤不完全因為預設執行100次,但還是後面把迴圈的指令條件該作了m!=4
  • m和n的錯誤輸入如果為字母將會帶來很大的問題,在上面未修改為m!=4的時候,尚迴圈100次停止,目前需要執行錯誤行為完畢。後期還需調整。

目前的main.c為:

#include <iostream>
#include <vector>
#include "bst.h"
#include "VisualTree.h"


int main(int argc, char*argv[])
{
    typedef int                 ValueType;
    typedef TreeNode<ValueType> NodeType;
    
    BSTree<ValueType>                tree;
    VisualTree<NodeType, ValueType>  vtree(&NodeType::parent,
                                           &NodeType::left,
                                           &NodeType::right,
                                           &NodeType::val);
    
    int m=0,n=0;
    while(m!=4){
        printf("-----------------------------------------------\n");
        printf("--------------------請輸入指令-------------------\n");
        printf("---      插入:1,num             查詢:2,num    ---\n");
        printf("---      刪除:3,num             退出:4        ---\n");
        printf("---                     (注:num設定為0~100) ---\n");
        printf("-----------------------------------------------\n");
        scanf("%d,%d",&m,&n);
        printf("\n-----------------------------------------------\n");
        //健壯性1
        if(m!=1&&m!=2&&m!=3&&m!=4){
            printf("輸入指令錯誤,請重新輸入。\n");
            continue;
        }
        //健壯性2
        if(n<0||n>100){
            printf("輸入數字錯誤,請重新輸入。\n");
            continue;
        }
        if(m==1){
            ValueType v =n;
            tree.insert(v);
            std::cout << "insert : " << v << std::endl;
            vtree.draw(tree.root());
        }else if (m==2){
            ValueType search =n;
            if(!tree.find(search)){
                        printf("未找到\n");
                    }else{
                        printf("已找到\n");
                    }
        }else if(m==3){
            ValueType search=n;
            if(!tree.find(search)){
                printf("未找到\n");
            }else{
                printf("已找到,並刪除\n");
            }
            TreeNode<int> *p=tree.find(search);
            tree.remove(p);
            vtree.draw(tree.root());
        }else if(m==4){
            break;
        }
        
    }
    return 0;
}

可以說,已經完成了階段三的工作。

六、階段四:加入調整規則使二叉搜尋樹變為平衡樹

七、階段五:動態顯示二叉平衡樹

使用上上篇部落格裡對二叉搜尋樹轉換為二叉平衡樹的思想,時間關係找了一篇別人的博文,程式碼稍作改動,內容如下:

AVLTree.h

//
//  AVLTree.h
//  DrawvBinarySearchTree
//
//  Created by 張立鵬 on 2018/11/1.
//  Copyright © 2018年 張立鵬. All rights reserved.
//

#ifndef AVLTree_h
#define AVLTree_h

#include <iostream>
#include <algorithm>

using namespace std;
#pragma once

//平衡二叉樹結點
template <typename T>
struct AvlNode
{
    T data;
    int height; //結點所在高度,校驗平衡度
    AvlNode<T> *left;
    AvlNode<T> *right;
    AvlNode<T>(const T theData) : data(theData), left(NULL), right(NULL), height(0){}
};
static int shuchu[50]={0};
static int s=0;
//AVLTree
template <typename T>
class AvlTree
{
    public:
        AvlTree<T>(){}
        ~AvlTree<T>(){}
        AvlNode<T> *root;
        //插入結點
        void Insert(AvlNode<T> *&t, T x);
        //刪除結點
        bool Delete(AvlNode<T> *&t, T x);
        //查詢是否存在給定值的結點
        bool Contains(AvlNode<T> *t, const T x) const;
        //中序遍歷
        void InorderTraversal(AvlNode<T> *t);
        //前序遍歷
        void PreorderTraversal(AvlNode<T> *t);
        //最小值結點
        AvlNode<T> *FindMin(AvlNode<T> *t) const;
        //最大值結點
        AvlNode<T> *FindMax(AvlNode<T> *t) const;
    private:
        //求樹的高度
        int GetHeight(AvlNode<T> *t);
        //單旋轉 左
        AvlNode<T> *LL(AvlNode<T> *t);
        //單旋轉 右
        AvlNode<T> *RR(AvlNode<T> *t);
        //雙旋轉 右左
        AvlNode<T> *LR(AvlNode<T> *t);
        //雙旋轉 左右
        AvlNode<T> *RL(AvlNode<T> *t);
      };

template <typename T>
AvlNode<T> * AvlTree<T>::FindMax(AvlNode<T> *t) const
{
        if (t == NULL)
                return NULL;
        if (t->right == NULL)
                return t;
        return FindMax(t->right);
    }
template <typename T>
AvlNode<T> * AvlTree<T>::FindMin(AvlNode<T> *t) const
{
        if (t == NULL)
            return NULL;
        if (t->left == NULL)
                return t;
        return FindMin(t->left);
    }

template <typename T>
int AvlTree<T>::GetHeight(AvlNode<T> *t)
{
    if (t == NULL)return -1;
    else       return t->height;
    
}

//單旋轉
//左左插入導致的不平衡
template <typename T>
AvlNode<T> * AvlTree<T>::LL(AvlNode<T> *t)
{
        AvlNode<T> *q = t->left;
        t->left = q->right;
        q->right = t;
        t = q;
        t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
        q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
        return q;
    }
//單旋轉
//右右插入導致的不平衡
template <typename T>
AvlNode<T> * AvlTree<T>::RR(AvlNode<T> *t)
{
        AvlNode<T> *q = t->right;
        t->right = q->left;
        q->left = t;
        t = q;
        t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
        q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
        return q;
    }
//雙旋轉
//插入點位於t的左兒子的右子樹
template <typename T>
AvlNode<T> * AvlTree<T>::LR(AvlNode<T> *t)
{
        //雙旋轉可以通過兩次單旋轉實現
        //對t的左結點進行RR旋轉,再對根節點進行LL旋轉
        RR(t->left);
        return LL(t);
    }

//雙旋轉
//插入點位於t的右兒子的左子樹
template <typename T>
AvlNode<T> * AvlTree<T>::RL(AvlNode<T> *t)
{
        LL(t->right);
        return RR(t);
    }
template <typename T>
void AvlTree<T>::Insert(AvlNode<T> *&t, T x)
{
        if (t == NULL)
                t = new AvlNode<T>(x);
            else if (x < t->data)
                {
                        Insert(t->left, x);
                        //判斷平衡情況
                        if (GetHeight(t->left) - GetHeight(t->right) > 1)
                            {
                                    //分兩種情況 左左或左右
                                    if (x < t->left->data)//左左
                                            t = LL(t);
                                        else                  //左右
                                               t = LR(t);
                                        }
                   }
        else if (x > t->data)
           {
                   Insert(t->right, x);
                    if (GetHeight(t->right) - GetHeight(t->left) > 1)
                        {
                               if (x > t->right->data)
                                        t = RR(t);
                                    else
                                           t = RL(t);
                                   }
                 }
       else
              ;//資料重複
        t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
   }

 template <typename T>
 bool AvlTree<T>::Delete(AvlNode<T> *&t, T x)
 {
         //t為空 未找到要刪除的結點
        if (t == NULL)
                return false;
        //找到了要刪除的結點
         else if (t->data == x)
            {
                     //左右子樹都非空
                   if (t->left != NULL && t->right != NULL)
                         {//在高度更大的那個子樹上進行刪除操作
                   
                                //左子樹高度大,刪除左子樹中值最大的結點,將其賦給根結點
                                 if (GetHeight(t->left) > GetHeight(t->right))
                                   {
                                            t->data = FindMax(t->left)->data;
                                            Delete(t->left, t->data);
                                       }
                                 else//右子樹高度更大,刪除右子樹中值最小的結點,將其賦給根結點
                                 {
                                       t->data = FindMin(t->right)->data;
                                           Delete(t->right, t->data);
                                       }
                            }
                    else
                        {//左右子樹有一個不為空,直接用需要刪除的結點的子結點替換即可
                             AvlNode<T> *old = t;
                              t = t->left ? t->left: t->right;//t賦值為不空的子結點
                                 delete old;
                        }
            }
       else if (x < t->data)//要刪除的結點在左子樹上
            {
                   //遞迴刪除左子樹上的結點
                    Delete(t->left, x);
                     //判斷是否仍然滿足平衡條件
                   if (GetHeight(t->right) - GetHeight(t->left) > 1)
                         {
                                if (GetHeight(t->right->left) > GetHeight(t->right->right))
                                    {
                                            //RL雙旋轉
                                            t = RL(t);
                                        }
                                else
                                    {//RR單旋轉
                                            t = RR(t);
                                    }
                            }
                    else//滿足平衡條件 調整高度資訊
                        {
                                t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
                            }
                }
        else//要刪除的結點在右子樹上
        {
                //遞迴刪除右子樹結點
                Delete(t->right, x);
                //判斷平衡情況
                if (GetHeight(t->left) - GetHeight(t->right) > 1)
                {
                    if (GetHeight(t->left->right) > GetHeight(t->left->left))
                        {
                                //LR雙旋轉
                                t = LR(t);
                        }
                    else
                        {
                            //LL單旋轉
                            t = LL(t);
                        }
                }
               else//滿足平衡性 調整高度
                {
                        t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
                }
        }
     return true;
} 
//前序遍歷
template <typename T>
void AvlTree<T>::PreorderTraversal(AvlNode<T> *t)
{
    if (t)
    {
        shuchu[s]=t->data;
        s++;
        PreorderTraversal(t->left);
        PreorderTraversal(t->right);
    }
   
}
#endif /* AVLTree_h */

執行介面如下:

八、階段六:使用多種方式顯示二叉平衡樹

1.java 2D  

 畫板部分參考:https://blog.csdn.net/qq_40286361/article/details/78014795,這是使用java2D畫二叉樹的,後面也可以對前面階段的工作進行拓展和使用。

       既然如此,我們先對找到的部落格進行研究,程式碼也放到這邊進行參考和學習。

MutableInteger.java  用以傳遞可變整數,物件是函式返回值

package Btree;
public class MutableInteger {
    public int value;
    public MutableInteger(int x) { value = x; }
    public MutableInteger() { value = 0; }
    public boolean equals(int x) {
        if ( x==value )
            return true;
        else
            return false;
    }
    public int getValue() { return value; }
    public void setValue(int value) { this.value = value; }
}

Tnode.java

package tree;

import Btree.MutableInteger;

import java.util.LinkedList;
import java.util.List;

public class Tnode {
    private String name;                //該結點名字
    private int layer = 0;              //該結點層級
    private int x = -1;                 //x座標
    private List<Tnode> childs = null;  //儲存該結點的孩子

    public Tnode(String name) { this.name = name; }
    public Tnode() { this.name = null; }
    public void add(Tnode n) {
        if (childs==null)
            childs = new LinkedList<Tnode>();//這裡可以改為ArrayList
        n.layer = this.layer + 1;
        setChildLayer(n);
        childs.add(n);
    }
    private void setChildLayer(Tnode n) {//遞迴設定層級,深度優先
        if (n.hasChild()) {
            List<Tnode> c = n.getChilds();
            for (Tnode node : c) {
                node.layer = n.layer + 1;
                setChildLayer(node);
            }
        }
    }
    public void CoordinateProcess(MutableInteger maxX, MutableInteger maxY) { CoordinateProcess(this, maxX, maxY); }
    public static void CoordinateProcess(Tnode n, MutableInteger maxX, MutableInteger maxY) {
        //max其實是用來佈置畫布的大小而設定的返回值
        //預設的根節點座標是(0,0),即x=0,layer=0
        setx(n, new MutableInteger(0), maxX, maxY);
    }
    private static void setx(Tnode n, MutableInteger va, MutableInteger maxX, MutableInteger maxY) {//va其實只是用來儲存中間結果用來呼叫的
        if (n.hasChild()) {
            List<Tnode> c = n.getChilds();
            c.get(0).x = va.value;
            setx(c.get(0), va, maxX, maxY);
            for (int i=1; i<c.size(); i++) {
                setx(c.get(i), va, maxX, maxY);
            }
            n.x = c.get(0).x;//本結點的x是第一個孩子的x
        } else {
            n.x = va.value++;
        }
        //儲存最大的x,y返回
        if (n.getX()>maxX.value) {
            maxX.value = n.getX();
        }
        if (n.getLayer()>maxY.value) {
            maxY.value = n.getLayer();
        }
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getLayer() { return layer; }
    public int getX() { return x; }
    public void setLayer(int layer) { this.layer = layer; }
    public List<Tnode> getChilds() { return childs; }
    public void setChilds(List<Tnode> childs) { this.childs = childs; }
    public boolean hasChild() { return childs==null ? false : true; }
    public void printAllNode(Tnode n) {//遞迴列印所有結點,深優
        System.out.println(n.toString());
        if (n.hasChild()) {
            List<Tnode> c = n.getChilds();
            for (Tnode node : c) {
                printAllNode(node);
            }
        }
    }
    public void printAllNode() { printAllNode(this); }
    public String getAllNodeName(Tnode n) {
        String s = n.toString()+"/n";
        if (n.hasChild()) {
            List<Tnode> c = n.getChilds();
            for (Tnode node : c) {
                s += getAllNodeName(node)+"/n";
            }
        }
        return s;
    }
    public String getAllNodeName() { return getAllNodeName(this); }
    public String toString() { return name; }
}

TreePanel.java

package Btree;
import java.awt.*;
import java.util.List;

import javax.swing.JPanel;

public class TreePanel extends JPanel {

    private static final long serialVersionUID = 1L;

    private Tnode tree;             //儲存整棵樹
    private int gridWidth = 170;    //每個結點的寬度
    private int gridHeight = 20;    //每個結點的高度
    private int vGap = 50;          //每2個結點的垂直距離
    private int hGap = 30;          //每2個結點的水平距離

    private int startY = 10;        //根結點的Y,預設距離頂部10畫素
    private int startX = 10;        //根結點的X,預設距離左端10畫素

    //改進之後的程式呢就不是原文的對對齊方式啦,所以下面幾行是沒用的
    //private int childAlign;                     //孩子對齊方式
    //public static int CHILD_ALIGN_ABSOLUTE = 0; //相對Panel居中
    //public static int CHILD_ALIGN_RELATIVE = 1; //相對父結點居中

    private Font font = new Font("微軟雅黑",Font.BOLD,14);  //描述結點的字型

    private Color gridColor = Color.BLACK;      //結點背景顏色
    private Color linkLineColor = Color.BLACK;  //結點連線顏色
    private Color stringColor = Color.WHITE;    //結點描述文字的顏色
    /*放棄了原文的內容,這是由於我們只有一種畫法,而不是中間對其或是左對齊等
    public TreePanel() { this(null,CHILD_ALIGN_ABSOLUTE); }
    public TreePanel(Tnode n) { this(n,CHILD_ALIGN_ABSOLUTE); }
    public TreePanel(int childAlign) { this(null,childAlign); }
    public TreePanel(Tnode n, int childAlign) {
        super();
        setTree(n);
        this.childAlign = childAlign;
    }
    */
    public TreePanel() { this(null); }
    public TreePanel(Tnode n) {
        super();
        setTree(n);
    }
    public void setTree(Tnode n) { tree = n; }

    //重寫,呼叫自己的繪製方法
    public void paintComponent(Graphics g) {
        //startX = (getWidth()-gridWidth)/2;//這是居中方式的設定,放棄原文方法
        super.paintComponent(g);
        g.setFont(font);
        drawAllNode(tree, g);
    }

    /**
     * 遞迴繪製整棵樹
     * n 被繪製的Node
     * xPos 根節點的繪製X位置
     * g 繪圖上下文環境
     */
    public void drawAllNode(Tnode n, Graphics g) {
        int y = n.getLayer()*(vGap+gridHeight)+startY;
        int x = n.getX()*(hGap+gridWidth)+startX;
        int fontY = y + gridHeight - 5;     //5為測試得出的值,你可以通過FM計算更精確的,但會影響速度


        g.setColor(gridColor);
        g.fillRoundRect(x, y, gridWidth, gridHeight, 10, 10);    //畫結點的格子

        g.setColor(stringColor);
        g.drawString(n.toString(), x+5, fontY);       //畫結點的名字


        if (n.hasChild()) {
            g.setColor(linkLineColor);
            g.drawLine(x+gridWidth/2, y+gridHeight, x+gridWidth/2, y+gridHeight+vGap/2);

            List<Tnode> c = n.getChilds();
            int i = 0;
            for (Tnode node : c) {
                int newX = node.getX()*(hGap+gridWidth)+startX; //孩子結點起始X
                g.setColor(linkLineColor);
                g.drawLine(newX+gridWidth/2, y+gridHeight+vGap/2, newX+gridWidth/2, y+gridHeight+vGap);
                drawAllNode(node, g);
                i++;
                if (i==c.size()) {
                    g.setColor(linkLineColor);
                    g.drawLine(x+gridWidth/2, y+gridHeight+vGap/2, newX+gridWidth/2, y+gridHeight+vGap/2);
                }
            }
        }
    }

    public Color getGridColor() { return gridColor; }
    public void setGridColor(Color gridColor) { this.gridColor = gridColor; }
    public Color getLinkLineColor() { return linkLineColor; }
    public void setLinkLineColor(Color gridLinkLine) { this.linkLineColor = gridLinkLine; }
    public Color getStringColor() { return stringColor; }
    public void setStringColor(Color stringColor) { this.stringColor = stringColor; }
    public int getStartY() { return startY; }
    public void setStartY(int startY) { this.startY = startY; }
    public int getStartX() { return startX; }
    public void setStartX(int startX) { this.startX = startX; }
    public int getGridWidth() { return gridWidth; }
    public void setGridWidth(int gridWidth) { this.gridWidth = gridWidth; }
    public int getGridHight() { return gridHeight; }
    public void setGridHeight(int gridHeight) { this.gridHeight = gridHeight; }
    public int getVGap() { return vGap; }
    public void setVGap(int vGap) { this.vGap = vGap; }
    public int getHGap() { return hGap; }
    public void setHGap(int hGap) { this.hGap = hGap; }

}

原博主的實現效果如下:

一個語法樹的部分

可見是可以實現的。

目前我要做兩件事情:

1.使用Java實現同樣的二叉樹輸出;

2.使用畫板實現二叉樹的輸出。

因為畫板是在使用Java窗體的基礎上定義各種畫面元素的位置,而位置的判斷常常伴隨著裂點等操作,因此是畫板程式碼和B-tree的結合。

首先我找來了graph2D列印二叉樹的程式碼(看來我有時間得把前面的工作拓展一下咯),並作以研究,經本人改動之後如下:

TreeNode.java

package gg;

public class TreeNode {
	String data;
	TreeNode left;
	TreeNode right;
	int layerNo, x;
	/*三個建構函式*/
	public TreeNode(String v) {
		data = v;
		left = null;
		right = null;
	}
	public TreeNode(String v, TreeNode l, TreeNode r) {
		data = v;
		left = l;
		right = r;
	}
	public TreeNode() {
		// 預設建構函式
	}

}

 

Mytree.java

package gg;
import gg.TreeNode;
public class Mytree {
	public int cx, cy;
	public int i = 0, j = 0;
	TreeNode root;							//根節點
	int maxDepth;							//最大深度

	Mytree(String v) {						//建構函式
		insert(v);							//自定義insert函式
	}

	void insert(String v) {
		TreeNode p = new TreeNode(), head;	//TreeNode
		TreeNode st[];
		st = new TreeNode[v.length() + 10];
		int top = -1, k = 0, j = 0;			//j為String v中字母的索引位置
		char ch;
		head = root;						// 建立的二叉樹初始時為空
		ch = v.charAt(j);//harAt(int index)方法是一個能夠用來檢索特定索引下的字元的String例項的方法.charAt()方法返回指定索引位置的char值。索引範圍為0~length()-1
		if (head == null) {					// 頭結點相當於一個虛擬的頭ROOT.
			head = new TreeNode(""+ch);
			j++;
			ch = v.charAt(j);
		}
		int i = v.length();
		while (j < v.length() - 1) 			// str未掃描完時迴圈
		{
			switch (ch) {
			case '(':
				top++;
				k = 1;
				break;
			case ')':
				head = st[top - k];
				top -= k;
				k = 1;
				break;
			case ',':
				head = st[top];
				top++;
				k = 2;
				break; // 上述選擇一個變數來確定有幾個節點
			default:
				p = new TreeNode("" + ch);
				// 已建立二叉樹根結點
				{
					switch (k) {
					case 1:
						head.left = p;
						st[top] = head;
						head = head.left;
						break;
					case 2:
						head.right = p;
						st[top] = head;
						head = head.right;// 插入,並且儲存一個數組中
						break;
					}
				}
			}
			j++;
			ch = v.charAt(j);
		}
		root = st[0];
	}
	public void calcX(TreeNode tn, int x) {
		tn.x = x;

		if (tn.left != null)
			calcX(tn.left, x - 1);

		if (tn.right != null)
			calcX(tn.right, x + 1);
	}

	public void calcX2(TreeNode tn, int x) {
		tn.x = x;
		int dx = (int) Math.pow(2, maxDepth - tn.layerNo - 1);

		if (tn.left != null)
			calcX2(tn.left, x - dx);

		if (tn.right != null)
			calcX2(tn.right, x + dx);
	}

	// 計算每個結點的x座標,採用前序遍歷

	// *******************************************************************
	public void calcDepth(TreeNode tn, int d) {
		tn.layerNo = d;
		if (maxDepth < d)
			maxDepth = d;
		d += 1;
		if (tn.left != null)
			calcDepth(tn.left, d);

		if (tn.right != null)
			calcDepth(tn.right, d);
	}

	// 計算每個結點的層號及樹的最大深度,採用前序遍歷

	// *************************************************************************
	public void printLayer() {
		calcDepth(root, 1); // 先計算出每個結點的層號及樹的最大深度maxDepth

		calcX2(root, (int) Math.pow(2, maxDepth - 1));

	}

}

study.java

package gg;
import java.awt.Color;
import java.awt.Event;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import gg.TreeNode;
import gg.Mytree;

public class study extends Frame {
	String temp = " ";// 下面交換字串有用。
	static String tree = "a(b(l,m),e(f(h,j),i(k,g)))";//自定義二叉樹
	Toolkit toolkit = Toolkit.getDefaultToolkit();

	public static void main(String args[]) {
		Frame f = new study();
		f.setTitle("二叉樹的圖形展示                                          ———WittPeng");
		f.setBackground(Color.LIGHT_GRAY);
		f.setBounds(1000, 500, 1000, 800);//x,y,width,height
		f.setVisible(true);				//窗體可視
		f.setResizable(false);
	}

	// 類似於資料結構中的連結串列
	public boolean handleEvent(Event evt) {
		if (evt.id == Event.WINDOW_DESTROY)
			System.exit(0);
		return super.handleEvent(evt);
	}

	public void paint(Graphics g) {
		int i = 0, j = 0;
		Mytree bt = new Mytree(tree);
		TreeNode Node, tNode[];
		int cx[], cy[];
		Node = bt.root;
		bt.printLayer();// 首先遍歷一下,得到座標初始化。
		int top = -1;
		tNode = new TreeNode[bt.maxDepth];// 為了下層遍歷出座標定義的一個數組
		cx = new int[tree.length() + 1];
		cy = new int[tree.length() + 1];
		cx[0] = 1;
		cy[0] = 1;

		if (bt.root != null) {
			top++;
			tNode[top] = bt.root;
			while (top > -1) {
				Node = tNode[top];
				top--;
				g.setColor(Color.PINK);//節點背景顏色
				g.fillOval(100 + Node.x * 40, Node.layerNo * 50, 40, 20);//x,y,width,height
				g.setColor(Color.BLACK);//節點字型顏色
				g.drawString(Node.data, 120 + Node.x * 40,
						Node.layerNo * 50 + 13);//String Str,x,y

				cx[i] = 120 + Node.x * 40;
				cy[i] = Node.layerNo * 50;// cx cy 兩座標統計節點的座標值,相同下標對應的XY座標為節點座標。

				i++;
				if (Node.right != null) {
					top++;
					tNode[top] = Node.right;
				}
				if (Node.left != null) {
					top++;
					tNode[top] = Node.left;
				}
			}

		}
		// 非遞迴遍歷出樹的結點
		g.setColor(Color.black);//線條顏色
		top = 0;
		int k = 1, s = 0, a = 0, b = i;

		s = cx[1] - c