目的:为了调试查看二叉树的数据,最直观的方式就是以图形方式显示出来,一目了然
方式:
一,命令行程序中以黑窗口字符形式显示
略
二,windows窗口中以图形方式显示
提供一个类CTreeShow来实现显示指定的二叉树
显示时要解决的重点问题就是各个节点的位置按排,因此定义显示节点为
struct CTreeShowNode{
string text; //代表节点的文字
int x,y;//该节点的显示位置(x,y单位采用步长,而不是像素,便于显示时调整间距)
CTreeShowNode *left,*right;//左右节点
}
整个过程要分三步:
1,从原始二叉树数据重新构建CTreeShowNode的显示二叉树
重新构建时需要原始二叉树的结构,一般来说原始二叉树结构各人定义的不一致,如果我们要提供一个统一的二叉树显示功能类,则不能强制规定原始二叉树格式,需要协商一个接口办法。考虑先将原始二叉树序列化成字符串,再由CTreeShow类反序化构建新的显示树
2,安排CTreeShowNode树的各节点位置x,y
y代表深度位置,按树的层级很好实现。
x代表横向位置,一般左子树在左,根节点在中间,所以可以考虑采用中序遍历的递归方式,将左子树起点安排在x,y+1的位置,当前节点安排在newx(指左子树安排后的下一个位置),y的位置,右子树起点安排在newx+1,y+1的位置。
int arrange_pos(CTreeShowNode* p,int x,int y)//递归函数,中序遍历,安排各节点的显示位置x,y,返回新空位置
{
if(p==NULL) return x;
if(p->left) x=arrange_pos(p->left,x,y+1);
p->x=x; p->y=y; x++;
if(p->right) x=arrange_pos(p->right,x,y+1);
return x;
}
3,将CTreeShowNode树画在指定CDC上
整个文件TreeShow.h如下
#if !defined(AFX_TREESHOW_H__7B4BD16D_2BCC_48A4_9D8E_2B9E440B211D__INCLUDED_)
#define AFX_TREESHOW_H__7B4BD16D_2BCC_48A4_9D8E_2B9E440B211D__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <string>
//
class CTreeShow//二叉树显示类
{
private:
struct CTreeShowNode{//树显示节点
string text;int x,y;
CTreeShowNode *left,*right;
}*m_tree;
void loadtree(TreeNode* psrc,CTreeShowNode** pdst)//递归函数,复制树结构
{
if(psrc==NULL) return;
CString s;if(isoperator(psrc)) s.Format("%c",(int)psrc->value);else s.Format("%d",(int)psrc->value);
CTreeShowNode* p=new CTreeShowNode;if(p==NULL){puts("out of mem");return;}
p->text=s;p->x=p->y=0;p->left=p->right=NULL;*pdst=p;
//
loadtree(psrc->left,&p->left);loadtree(psrc->right,&p->right);
}
int arrange_pos(CTreeShowNode* p,int x,int y)//递归函数,中序遍历,安排各节点的显示位置x,y
{
if(p==NULL) return x;
if(p->left) x=arrange_pos(p->left,x,y+1);
p->x=x; p->y=y; x++;
if(p->right) x=arrange_pos(p->right,x,y+1);
return x;
}
CDC* m_pdc;int width_pad,height_pad;
void draw_tree(CTreeShowNode* p)//递归函数,前序遍历显示
{
if(p==NULL) return;
int x=p->x*width_pad,y=p->y*height_pad;
CRect rc(CPoint(x,y),m_pdc->GetTextExtent(p->text.c_str()));m_pdc->Rectangle(&rc);//画框
m_pdc->TextOut(x,y,p->text.c_str());
//画线
if(p->left){m_pdc->MoveTo(x,y);m_pdc->LineTo(p->left->x*width_pad,p->left->y*height_pad);}
if(p->right){m_pdc->MoveTo(x,y);m_pdc->LineTo(p->right->x*width_pad,p->right->y*height_pad);}
//
draw_tree(p->left);draw_tree(p->right);
}
void free_tree(CTreeShowNode* p)//递归函数,后序遍历删除
{
if(p==NULL) return;
free_tree(p->left);free_tree(p->right);delete p;
}
public:
CTreeShow():m_tree(NULL){}
virtual ~CTreeShow(){free_tree(m_tree);}//释放
void show_tree(TreeNode* pHead,CDC* pdc,int width_pad1=20,int height_pad1=40)
{
if(pHead==NULL || pdc==NULL) return;
m_pdc=pdc;width_pad=width_pad1;height_pad=height_pad1;
if(m_tree){free_tree(m_tree);m_tree=NULL;}//担心有人反复调用show_tree
loadtree(pHead,&m_tree);
arrange_pos(m_tree,0,0);
draw_tree(m_tree);
}
void refresh(){draw_tree(m_tree);}
};
#endif
用法:
将代码保存到TreeShow.h文件中,加入工程,使用时
#include "TreeShow.h"
CTreeShow show;show.show_tree(pHead,pdc);