我放在chinaunix上的源码:点击下载 原来代码有错,现在补上
这是画二叉树的改良版本,可能你需要看前面和画树相关的那篇文章描述才能看懂这篇文章。
由于前一个版本画树时是按照满树的位置来画的,所以如果一棵树如果非常高而节点又比较少时,其实一个屏幕画不了,很多节点就超出屏幕范围了,其缺点不公是在于此,还在于当我用它来表示红黑树时,不能用不同颜色来画,因此这个版本 的实现加入了一个扩展处理的函数指针,也就是说在画每个节点时可以调用客户函数来作一些客户定制的操作,比如可以根据红黑节点的性质设置当前的画线颜色。
当然,这个版本的主要难点在于怎么样使树可以自动收缩以充分利用有限屏幕空间画出我们的二叉树。
这个版本是在前一个版本 的基础上修改实现的,自动收缩调整思路大体如下:
假设当前要画的节点为 X,其父节点为T,其左孩子为L,右孩子為R。設一棵滿树从上到下为第1层,第2层....,第i层,根为每一层。注意,是满树,如果也不是满树也假设用满树中节点位置来标识某个节点。每一层从左到右为第1个节点,第2个节点,第3个节点,....,第j个节点.则树中.则树的节点可以用向量(i,j)来标识.
中心思路是:假设X已经按照满树在屏幕中画出,现在X通过一个偏移向量shift(rs,ls)向中间收缩,通过一个向量(extendx,extendy)进行节点在水平和垂直方向节点间的距离的自由伸缩,通过个向量(offx,offy)平移整棵树。实现shift为一个结构体
struct amount_shift
{
double ls;
double rs;
};
typedef struct amount_shift Shift;
typedef struct amount_shift P_Shift;
ls和rs不直接用像素而是用单元格表示,每个单元格的单位长度是根据树收缩后的大小计算出来的,设其单位为w,则收缩量左右子树的收缩量用像素表示应该是 Shift.ls*w和Shift.rs*w
设参数传到当前节点X时,X节点需要和其父节点平移pshift(也是单位长度),此时有三种情况:
1.X为根,则当前x用当预定义的平移里pshift在屏幕中画出。
并递归的计算出Shift,Shift.ls表示左树可向中间平收缩的空间量,同理Shift.rs表示右子树向中间的收缩量
2.X在树的左半部分上,则节点X应该向右平移 pshift+Shift.rs,而Shift.ls表示其父节点应该还需要向右的平移量
当R为空时,Shift.rs=pow(2,h-i)-0.5,h为树的高度,i表示当前节点所在的层,否则Shift.rs=DB_DrawNode(R).rs,
注:DB_DrawNode是递归实现的,它向父节点返回当前节点一个Shift向量值。
同时 当L为空时,Shift.ls=pow(2,h-i)-0.5,否则Shift.ls=DB_DrawNode(L).ls
3.X在树的左半部分上,则节点X应该向左平移 pshift+Shift.ls,而Shift.rs表示其父节点应该还需要向左的平移量
当R为空时,Shift.ls=-pow(2,h-i)+0.5,h为树的高度,i表示当前节点所在的层,否则Shift.ls=DB_DrawNode(R).ls,
注:DB_DrawNode是递归实现的,它向父节点返回当前节点一个Shift向量值。
同时 当L为空时,Shift.rs=-pow(2,h-i)+0.5,否则Shift.rs=DB_DrawNode(L).rs
可见,DB_DrawNode函数就是递归的画出了所有的节点,画的顺序是从叶子向根。第一次调用DB_DrawNode时,传入一个整棵树的偏移量pshift,这时是第一种情况,其左子树是第二种情况,其右子树是第三种情况。
判断其左右子树的方法为 tflag=(j<=pow(2,i-2));前面已经说了,i为节点所处的层,j是假设其为满树时的从左到右的标号。在这里再提一下,假设当前节点是第i层的第j个节点,则其左孩子是是第i+1动的 2*j-1个节点,其右孩子是第i+1层的第2*j个节点.
对当前节点,如果其左孩子不空则用一个函数DB_DrawStick画出两个节点间的连线;对其右孩子作同样的处理。DB_DrawStick需要用的参数为i,j,当前节点的偏移,孩子节点的偏移,还有就是伸缩向量(shiftx,shifty),平移
向量(offx,offy),而画节点的圆还是是计算完这些偏移得到实现的位置后传给函数画出。
这三种情况在递归实现的函数DB_DrawNode中非常清晰。请参考源代码。
调用示例:
struct AVL_TreeNode
{
int height;
AVL_Element e;
AVL_Tree left;
AVL_Tree right;
};
/******define some function to show the tree*******/
int getkey(void * t)
{
return ((AVL_Tree)t)->e;
}
void * getchild(void * t,int mode)
{
if(mode==DB_LEFTCHILD)return ((AVL_Tree)t)->left;
else return ((AVL_Tree)t)->right;
}
调用 : DB_DrawBitTree2(t,getchild,getkey,NULL,0,0,100,100);
(0,0)是收缩量,表示不收缩,100,100表示整棵树在(100,100)处画,getchild和getkey就不用说了吧?^_^
源代码的说明:请保留作者信息,源文都是就英文注释的(chinese-englis^_^),因为我的linux环境还没有安装中文平台。编译的前提是安装svgalib,并且要链接库 libvag和libvgagl
/********************************** *
*author:Felix
*last update:Mon Jan 14 11:51:31 EST 2008
*Address me and exchange our idea ^_^,QQ:349871604,e-mail:lzqziliao2004@163.com
*description:
*
*
*
*/
#ifndef ___DRAWBITTREE___
#define ___DRAWBITTREE___
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
/* default */
#define _GNUC_
/*select the platform and compiler*/
#ifdef _GNUC_
#define ___USESVGALIB___
#elif defined(_MSC_VER)
#define ___USEMSVCPP___
#endif
/*end*/
#ifdef __cplusplus
extern "C"
{
#endif
#ifdef ___USESVGALIB___
#include<vga.h>
#include<vgagl.h>
#ifndef VGA_H
# error Svgalib not found.The code work on svgalib in linux
#endif
#define DB_GetWidth() (vga_getxdim())
#define DB_GetHeight() (vga_getydim())
#define DB_vga_Init() vga_DefaultInit()
#define DB_vga_Final() vga_DefaultFinal()
/*
All graphics are based on DB_drawline.
*/
#define DB_drawline(x1,y1,x2,y2) vga_drawline((x1),(y1),(x2),(y2))
#elif defined(__USEMSVCPP__)/* Use Microsoft Visual c++*/
#endif
#define DB_LEFTCHILD 1
#define DB_RIGHTCHILD 2
#define DB_CIRCLE_COLOR 5
/*a node's maximum area of retangle:**/
#define MAX_NODE 50
#define MIN_NODE 10
typedef void * DB_GetChild2(void * p,int mode);
typedef int DB_GetKey2(void * p);
/* The function type that conduct the node before being drawed*/
typedef int DB_Travel(void *node);
/*
*t:the tree to be draw
*fgetKey:pointer to a funtion that return a key of a node
*fgetChild:pointer to a function that return a pointer to a left child of a node
* extendx: neighbor node in horizon extends by extendx
* extendy: neighbor node in vertical extends by extendy
* x,y: the up-left coordinate
* return value: a pointer to a struct like:
* struct shift
* {
* int left;
* int right;
* }
* left- the left half tree saves left pixels space .
* right - the right half tree saves right pixels space .
*/
void * DB_DrawBitTree2(void * t, DB_GetChild2 fgetChild, DB_GetKey2 fgetKey,DB_Travel fTravel,int extendx,int extendy,int x,int y);
#ifdef __cplusplus
}
#endif
#endif
/*last update:Sun Feb 21 1:00:31 EST 2008
*Address me and exchange our idea ^_^,QQ:349871604,e-mail:lzqziliao2004@163.com
*description:
*
*
*
*/
#include "drawBitTree2.h"
#include <math.h>
/*
new function and structure of version 2
*/
struct Amount_Shift
{
double ls;
double rs;
};
typedef struct Amount_Shift * P_Shift;
typedef struct Amount_Shift _Shift;
#define AS(ps) (ps->ls+ps->rs)
/*calculate the horizontal amount of node space that can be save*/
static P_Shift DB_HShift(void *t,DB_GetChild2 fgetChild,int height);
static inline int DB_LeafNode(void * t,DB_GetChild2 fgetChild);
P_Shift DB_HShift2(void *t,DB_GetChild2 fgetChild,int i,int j,int h);
/**end**/
/*
* For tranplanting,I define this as the vga enviroment's default functions for
* initilizing and finalizing.
*/
void vga_DefaultInit();
void vga_DefaultFinal();
/*
end
*/
static int DB_GetTreeHeight(void * t,DB_GetChild2 fgetChild);
static void DB_DrawCircle(int x,int y,int r,int key);
static void DB_DrawStick(int i,int j,int h,int r,int w,int mode,int shifty,double curs, double next,int extendx,int extendy,int offx,int offy);
static P_Shift DB_DrawNode(void *t,DB_GetChild2 fgetChild,DB_GetKey2 fgetKey,int i,int j,int r,int h,int w,double shifty,double pshift,DB_Travel fTravel,int extendx,int extendy,int offx,int offy);
static void DB_DrawStick(int i,int j,int h,int r,int w,int mode,int shifty,double curs ,double next,int extendx,int extendy,int offx,int offy)
{
#define V_DIS (w+shifty+extendy)
#define W_X (w+extendx)
#define H_Y (w+extendy+shifty)
#define H_DIS ((w+extendx)*(p_h_i/2-((next>curs)?next-curs:curs-next)))
double x1,x2,y1,y2;
double x,y;/*The parent node's coordinate*/
double p_h_i;/*store the value pow(2,h-i)*/
double cosa,sina,tana;
p_h_i=pow(2,h-i);
/*ls
you can see that the variable tana and the base posistion x and y here
are critically important
*/
/*
tana=V_DIS/((w+extendx)*(p_h_i/2-((next>curs)?next-curs:curs-next)));
*/
tana=V_DIS/H_DIS;
cosa=sqrt(1/(1+tana*tana));
sina=sqrt(1-cosa*cosa);
x=p_h_i*W_X*(2*j-1)+curs*W_X+offx;
y=(i-1)*(V_DIS)+H_Y+offy;
/**/
y1=y+r*sina;
y2=y+V_DIS-r*sina;
x1=(mode==DB_LEFTCHILD)?x-r*cosa: x+r*cosa;
x2=(mode==DB_LEFTCHILD)?x-H_DIS+cosa*r:x+H_DIS-r*cosa;
DB_drawline(x1,y1,x2,y2);
}
static void DB_DrawCircle(int x,int y,int r,int key)
{
int i;
int x1,y1,x2,y2;
x1=x-r;
y1=y-sqrt(r*r-(x-x1)*(x-x1));
for(i=x-r+1;i<=x+r+1;i++)
{
x2=i;
y2=y-sqrt(r*r-(x-x1)*(x-x1));
DB_drawline(x1,y1,x2,y2);
DB_drawline(x1,y1+2*(y-y1),x2,y2+2*(y-y2));
x1=x2;y1=y2;
}
}
/*
* Suppose current node is t and the shift amount of space from it's
* parent is s.Then,
* If t's left node is NULL,shift the whole tree to the center by
* the new-added amount of space ,adding up s.
* If t's right node is NULL,shift t to the center by s adding up the
* new-added amount of space and it's child by s only
*/
/*
* 2008-2-21 by felix
* DB_DrawNode
* Algorithm description
* Supose the current node of the tree T is X with its left node L and right node R.
* There are three condition:
* X is in T's left-half tree:
* X will shift along with R's shift rs and X's parent should shift with it
* adding up L's shift ls
* X is in T's right-half tree:
* X will shift along with L's shift ls and X's parent should shift with it
* adding up R's shift rs
* X is the root:
* X shifts with the pre-given shift pshift and all it's child nodes will
* shift accordingly.
* return : the amount of space it's parent should shift along with it.
*/
P_Shift DB_DrawNode(void *t,DB_GetChild2 fgetChild,DB_GetKey2 fgetKey,int i,int j,int r,int h,int w,double shifty,double pshift,DB_Travel fTravel,int extendx,int extendy,int offx,int offy)
{
void * pc1,*pc2;
P_Shift ps=(P_Shift)malloc(sizeof(_Shift));
P_Shift pl=NULL,pr=NULL;
double curs;
/*
* Decide the current node location.
* 0 stands for the left-haft tree and 1 stands for the right-half
* tree
*/
int tflag;
tflag=(j<=pow(2,i-2));
pc1=fgetChild(t,DB_LEFTCHILD);
pc2=fgetChild(t,DB_RIGHTCHILD);
ps->ls=ps->rs=0;
if(i==1)
{
curs=pshift;
if(pc1)
{
pl=DB_DrawNode(pc1,fgetChild,fgetKey,i+1,2*j-1,r,h,w,shifty,pshift,fTravel,extendx,extendy,offx,offy);
ps->ls=AS(pl);
DB_DrawStick(i,j,h,r,w,DB_LEFTCHILD,shifty,curs,curs+pl->rs,extendx,extendy,offx,offy);
} else ps->ls=pow(2,h-i)-0.5;
if(pc2)
{
pr=DB_DrawNode(pc2,fgetChild,fgetKey,i+1,2*j,r,h,w,shifty,pshift,fTravel ,extendx,extendy,offx,offy);
DB_DrawStick(i,j,h,r,w,DB_RIGHTCHILD,shifty,curs,curs+pr->ls,extendx,extendy,offx,offy);
ps->rs=AS(pr);
} else ps->rs=-pow(2,h-i)+0.5;
}
else
if(tflag)/*node of left tree*/ {
if(pc2)
{
pr=DB_DrawNode(pc2,fgetChild,fgetKey,i+1,2*j,r,h,w,shifty,pshift,fTravel ,extendx,extendy,offx,offy);
ps->rs=AS(pr);
DB_DrawStick(i,j,h,r,w,DB_RIGHTCHILD,shifty,pshift+ps->rs,pshift+pr->rs,extendx,extendy,offx,offy);
} else ps->rs=pow(2,h-i)-0.5;
if(pc1)
{
pl=DB_DrawNode(pc1,fgetChild,fgetKey,i+1,2*j-1,r,h,w,shifty,pshift+ps->rs,fTravel ,extendx,extendy,offx,offy);
ps->ls=AS(pl);
DB_DrawStick(i,j,h,r,w,DB_LEFTCHILD,shifty,pshift+ps->rs,pshift+ps->rs+pl->rs,extendx,extendy,offx,offy);
} else ps->ls=pow(2,h-i)-0.5;
curs=pshift+ps->rs;
}
else
{/*right tree*/
if(pc1)
{
pl=DB_DrawNode(pc1,fgetChild,fgetKey,i+1,2*j-1,r,h,w,shifty,pshift,fTravel ,extendx,extendy,offx,offy);
ps->ls=AS(pl);
DB_DrawStick(i,j,h,r,w,DB_LEFTCHILD,shifty,pshift+ps->ls,pshift+pl->ls,extendx,extendy,offx,offy);
} else ps->ls=-pow(2,h-i)+0.5;
if(pc2)
{
pr=DB_DrawNode(pc2,fgetChild,fgetKey,i+1,2*j,r,h,w,shifty,pshift+ps->ls,fTravel ,extendx,extendy,offx,offy);
ps->rs=AS(pr);
DB_DrawStick(i,j,h,r,w,DB_RIGHTCHILD,shifty,pshift+ps->ls,pshift+ps->ls+pr->ls,extendx,extendy,offx,offy);
} else ps->rs=-pow(2,h-i)+0.5;
curs=pshift+ps->ls;
}
if(fTravel)fTravel(t);
DB_DrawCircle((pow(2,h-i)*(2*j-1)+curs)*(extendx+w)+offx,(i)*(w+extendy)+offy,r,fgetKey(t));
gl_printf(pow(2,h-i)*(2*j-1)*(w+extendx)-2.0/3*r+curs*(w+extendx)+offx,i*(w+extendy+shifty)+offy,"%d",fgetKey(t));
if(pl)free(pl);
if(pr)free(pr);
return ps;
}
static int DB_GetTreeHeight(void * t,DB_GetChild2 fgetChild)
{
int lh,rh;
if(t==NULL)return -1;
lh=DB_GetTreeHeight(fgetChild(t,DB_LEFTCHILD),fgetChild);
rh=DB_GetTreeHeight(fgetChild(t,DB_RIGHTCHILD),fgetChild);
return lh>rh?lh+1:rh+1;
}
void * DB_DrawBitTree2(void * t, DB_GetChild2 fgetChild, DB_GetKey2 fgetKey,DB_Travel fTravel,int extendx,int extendy,int offx,int offy)
{
int h,w,r;
P_Shift prt, space;
DB_vga_Init();
r=(w>MIN_NODE&&w<MAX_NODE)?w*4/9:MAX_NODE/3;
r=(r>MAX_NODE/3)?MAX_NODE/3:r;
h=DB_GetTreeHeight(t,fgetChild);
space=DB_HShift(t,fgetChild,h);
w=1.0*DB_GetWidth()/(pow(2,h)-space->ls+space->rs);
if(w<MIN_NODE)
{
printf("tree's height is too large now,program exit");
return ;
}
w=w>MAX_NODE?MAX_NODE:w;
/*
gl_printf(500,500,"h=%d,ls=%lf,rs=%lf,w=%d,r=%d",h,space->ls,space->rs,w,r);
*/
prt=DB_DrawNode(t,fgetChild,fgetKey,1,1,r,h,w,/*shifty*/0,-space->ls,fTravel, extendx,extendy, offx, offy);
free(space);
/*convert it into pixels*/
prt->ls=prt->ls*w;
prt->rs=prt->rs*w;
DB_vga_Final();
/*
vga_getch();
vga_setmode(TEXT);
*/
return prt;
}
/*version 2*/
/*
* Calculate the horizontal amount of node space that can be saved
* do it to calculate the saved space:
* 1.Decend down the left child till it reach NULL and assign the
* value to LS.
* 2.Decend down the right child till it reach NULL and assign the
* value to RS.
* 3.Total space that can be shifted is TS.
* TS=LS+RS.
* Notice:
* A leaf node can save pow(2,h)-0.5 space.h is it's height.
*/
P_Shift DB_HShift(void *t,DB_GetChild2 fgetChild,int h)
{
return DB_HShift2(t,fgetChild,1,1,h);
}
P_Shift DB_HShift2(void *t,DB_GetChild2 fgetChild,int i,int j,int h)
{
void * pc1,*pc2;
P_Shift ps=(P_Shift)malloc(sizeof(_Shift));
P_Shift pl=NULL,pr=NULL;
int tflag;
tflag=(j<=pow(2,i-2));
pc1=fgetChild(t,DB_LEFTCHILD);
pc2=fgetChild(t,DB_RIGHTCHILD);
if(i==1)
{
if(pc1)
{
pl=DB_HShift2(pc1,fgetChild,i+1,2*j-1,h);
ps->ls=AS(pl);
} else ps->ls=pow(2,h-i)-0.5;
if(pc2)
{
pr=DB_HShift2(pc2,fgetChild,i+1,2*j,h);
ps->rs=AS(pr);
} else ps->rs=-pow(2,h-i)+0.5;
}
else
if(tflag){ /*node of left tree*/
if(pc2){
pr=DB_HShift2(pc2,fgetChild,i+1,2*j,h);
ps->rs=AS(pr);
} else ps->rs=pow(2,h-i)-0.5;
if(pc1)
{
pl=DB_HShift2(pc1,fgetChild,i+1,2*j-1,h);
ps->ls=AS(pl);
} else ps->ls=pow(2,h-i)-0.5;
}
else
{/*right tree*/
if(pc1)
{
pl=DB_HShift2(pc1,fgetChild,i+1,2*j-1,h);
ps->ls=AS(pl);
} else ps->ls=-pow(2,h-i)+0.5;
if(pc2)
{
pr=DB_HShift2(pc2,fgetChild,i+1,2*j,h);
ps->rs=AS(pr);
} else ps->rs=-pow(2,h-i)+0.5;
}
if(pl)free(pl);
if(pr)free(pr);
return ps;
}
static inline int DB_LeafNode(void * t,DB_GetChild2 fgetChild)
{
return (NULL==t||NULL!=fgetChild(t,DB_LEFTCHILD)||NULL!=fgetChild(t,DB_RIGHTCHILD))
?
0:1;
}
void vga_DefaultInit()
{
int vgamode;
vga_init();
vgamode=G800x600x256;
/*1072x600*/
/*
vgamode=100;
*/
vga_setmode(vgamode);
vga_setcolor(2);
gl_setcontextvga(vgamode);
gl_enableclipping();
gl_setfont(8,8,gl_font8x8);
gl_setwritemode(FONT_COMPRESSED+WRITEMODE_OVERWRITE);
gl_setfontcolors(0,2);
}
void vga_DefaultFinal()
{
vga_getch();
vga_setmode(TEXT);
}