我们可能需要将一个二叉树以图形化的方式显示出来,我实现了一个二叉树绘制器BinaryTreeDrawer,用于绘制前文实现的二叉树。
先看看这个绘制器的外貌:
public interface IBinaryTreeDrawer
{
void Initialize(DrawerParas paras) ;
void ResetGraphic(Graphics g) ; //在设备发生变化或尺寸改变时需重设设备句柄
void DrawBinaryTree(ISorttedBinaryTree tree ,int offsetLeft ,int offsetHigh) ; //offset用于滚动
void Zoom(double coeff) ;
Size GetCanvasSize(int binaryTreeDepth ,int radius) ; //根据树的深度和结点半径获得画布大小
int Radius{ get ;} //节点半径
}
//绘制器参数
public class DrawerParas
{
public Graphics Graphic = null; //在何种设备上绘制
public Color GraphicBackColor = Color.Gainsboro;
public int Radius = 10 ; //节点圆的半径
public Pen PenNode = new Pen(Color.Black ,1) ;
public Pen PenLine = new Pen(Color.Black ,1) ;
public SolidBrush BrushNode = new SolidBrush(Color.Pink);
public Font FontText = new Font("Arial", 9);
public SolidBrush BrushText = new SolidBrush(Color.Black);
}
接口没什么说的,实现如下:
public class BinaryTreeDrawer :IBinaryTreeDrawer
{
private DrawerParas drawParas = null ;
private ISorttedBinaryTree curTree = null ;
private int offsetX = 0 ;
private int offsetY = 0 ;
public BinaryTreeDrawer()
{
}
#region IBinaryDrawer 成员
public void Initialize(DrawerParas paras)
{
this.drawParas = paras ;
}
public void ResetGraphic(Graphics g)
{
if(this.drawParas != null)
{
this.drawParas.Graphic = g ;
}
}
public Size GetCanvasSize(int binaryTreeDepth ,int radius)
{
int width = radius * (int)(Math.Pow(2 ,binaryTreeDepth-1)) ;
int heigh = radius * binaryTreeDepth ;
return new Size(width ,heigh);
}
public void Zoom(double coeff)
{
this.drawParas.Radius = (int)(this.drawParas.Radius * coeff );
if(this.curTree != null)
{
this.DrawBinaryTree(this.curTree ,this.offsetX ,this.offsetY) ;
}
}
public void DrawBinaryTree(ISorttedBinaryTree tree ,int offsetLeft ,int offsetHigh)
{
if((this.drawParas == null) || (tree == null) ||(tree.Count == 0))
{
return ;
}
this.curTree = tree ;
this.offsetX = offsetLeft ;
this.offsetY = offsetHigh ;
try
{
this.drawParas.Graphic.Clear(this.drawParas.GraphicBackColor) ;
Point[][] position = this.GetNodePosition(tree.Depth) ;
Node root = tree.Root ;
this.DrawTree(root ,0 ,0 ,this.drawParas.Graphic ,position ,offsetLeft ,offsetHigh) ;
}
catch(Exception ee)
{
ee =ee ;
}
}
public int Radius
{
get
{
if(this.drawParas == null)
{
return 0 ;
}
return this.drawParas.Radius ;
}
}
#endregion
#region private
#region GetNodePosition
private Point[][] GetNodePosition(int depth)
{
Point[][] position = new Point[depth][] ;
for(int i=0 ;i<depth ;i++)
{
position[i] = new Point[(int)Math.Pow(2 ,i)] ;
}
//初始化最下一层
for(int j=0 ;j<Math.Pow(2 ,depth-1) ;j++ )
{
position[depth-1][j].X = 2*j ;//(int)(-( 2*(Math.Pow(2 ,depth-1)) -1 )/2) + 2*j ;
position[depth-1][j].Y = 2 * (depth) ;
}
//初始化其它层
if(depth >=2)
{
for(int i=depth-2 ;i>=0 ;i--)
{
for(int j=0 ;j<Math.Pow(2 ,i) ;j++ )
{
position[i][j].X = (position[i+1][2*j].X + position[i+1][2*j + 1].X)/2 ;
position[i][j].Y = 2 * (i+1) ;
}
}
}
return position ;
}
#endregion
#region DrawTree
private void DrawTree(Node root ,int rowIndex ,int colIndex ,Graphics g ,Point[][] position ,int offsetLeft ,int offsetHigh)
{
if(root == null)
{
return ;
}
int radius = this.drawParas.Radius ;
int x = position[rowIndex][colIndex].X*radius - offsetLeft + radius ;
int y = position[rowIndex][colIndex].Y*radius - offsetHigh + radius ;
g.FillEllipse(this.drawParas.BrushNode ,x ,y ,radius*2 ,radius*2) ;
g.DrawEllipse(this.drawParas.PenNode ,x ,y ,radius*2 ,radius*2) ;
g.DrawString(root.val.ToString() ,this.drawParas.FontText ,this.drawParas.BrushText ,x+ radius/4 ,y+ radius/4) ;
//递归
int x2 = 0 ;
int y2 = 0 ;
if(root.leftChild != null)
{
int col = 2*colIndex ;
x2 = position[rowIndex+1][col].X*radius - offsetLeft + radius ;
y2 = position[rowIndex+1][col].Y*radius - offsetHigh + radius ;
g.DrawLine(this.drawParas.PenNode ,x + radius/2 ,y + radius/2 ,x2 + radius/2 ,y2 + radius/2) ;
this.DrawTree(root.leftChild ,rowIndex+1 ,col ,g, position ,offsetLeft ,offsetHigh) ;
}
if(root.rightChild != null)
{
int col = 2*colIndex +1 ;
x2 = position[rowIndex+1][col].X*radius - offsetLeft + radius ;
y2 = position[rowIndex+1][col].Y*radius - offsetHigh + radius ;
g.DrawLine(this.drawParas.PenNode ,x + radius/2 ,y + radius/2 ,x2 + radius/2 ,y2 + radius/2) ;
this.DrawTree(root.rightChild ,rowIndex+1 ,col ,g, position ,offsetLeft ,offsetHigh) ;
}
}
#endregion
#endregion
}
整个实现最需要花心思的地方就是各个节点的布局。当每个节点的位置确定后,后续的工作就容易 了。