二叉树的实现与可视化

二叉树是一种重要的数据结构,它由一系列节点组成,每个节点最多有两个子节点,称为左子节点和右子节点。在本文中,将探讨如何在C#中创建二叉树,并使用GDI+技术在位图上绘制它。

二叉树的基本概念

二叉树中的每个节点都有一个唯一的值。例如,假设有一个二叉树,其根节点的值为776,那么776就是该节点的唯一值。

节点的添加规则

向二叉树中添加新节点时,从根节点开始,遵循以下规则:

  • 如果新节点的值小于根节点的值,则将其添加到根节点的左子节点。
  • 如果新节点的值大于根节点的值,则将其添加到根节点的右子节点。

无论添加到哪个节点,都遵循相同的过程。

节点的移除规则

从二叉树中移除节点时,遵循以下规则:

  • 如果节点没有子节点,则直接移除该节点。
  • 如果节点只有一个左子节点,则左子节点将取代其在树中的位置。
  • 如果节点有一个右子节点,且右子节点没有左子节点,则右子节点将取代其在树中的位置。
  • 如果节点有一个右子节点,且右子节点有左子节点,则需要移除右子节点的最左子节点(这将导致递归算法),并将其值替换到移除节点的位置。

使用代码

当应用程序启动时,它会随机添加一些节点到树中。用户可以通过按下添加按钮(或在文本框中按下回车键),将文本框中的值作为节点添加到二叉树中。通过按下创建按钮,可以创建一个新的二叉树。通过按下移除按钮,可以从树中移除包含文本框中值的节点。

通过按下“随机添加”按钮,可以向树中添加一个随机值作为节点。通过按下保存按钮,可以将当前在PictureBox上的图像保存到磁盘上。

要完全理解如何使用代码及其方法,请参考本文附带的主要源代码中的XML部分。

创建二叉树和绘制它

要创建二叉树并绘制它,请使用以下代码:

private BinaryTree tree; tree = new BinaryTree(); PaintTree();

要添加一个具有唯一值val的节点,请使用:

tree.Add(val);

添加节点的方法如下:

public void Add(int val) { if(val < Value) { if(Left == null) { Left = new Node(val); } else { Left.Add(val); } IsChanged = true; } else if(val > Value) { IsChanged = true; if(Right == null) { Right = new Node(val); } else { Right.Add(val); } } }

要移除一个具有值val的节点,请使用:

tree.Remove(val);

移除节点的方法按照前面描述的方式工作。它移除包含插入值的节点,同时也会移除其子节点。

public bool Remove(int val, out bool containedOnMe) { Node nodeToRemove; containedOnMe = false; var isLeft = val < Value; if(val < Value) { nodeToRemove = Left; } else if(val > Value) { nodeToRemove = Right; } else { if(Left != null) { Left.IsChanged = true; } if(Right != null) { Right.IsChanged = true; } containedOnMe = true; return false; } if(nodeToRemove == null) { return false; } bool containOnChild; var result = nodeToRemove.Remove(val, out containOnChild); if(containOnChild) { IsChanged = true; if(nodeToRemove.Left == null && nodeToRemove.Right == null) { if(isLeft) Left = null; else Right = null; } else if(nodeToRemove.Right == null) { if(isLeft) Left = nodeToRemove.Left; else Right = nodeToRemove.Left; } else { if(nodeToRemove.Right.Left == null) { nodeToRemove.Right.Left = nodeToRemove.Left; if(isLeft) Left = nodeToRemove.Right; else Right = nodeToRemove.Right; } else { Node nLeft = null; for(var n = nodeToRemove.Right; n != null; n = n.Left) { if(n.Left == null) { nLeft = n; } } bool temp; var v = nLeft.Value; Remove(nLeft.Value, out temp); nodeToRemove.Value = v; } } } return true; } return result; }

绘制操作非常简单:每个节点将绘制自己及其子节点,绘制方法是递归调用每个子节点来绘制自己,然后将结果传递给父节点,以便父节点可以绘制自己,这个过程对所有节点都会发生。

public Image Draw(out int center) { center = _lastCenter; if(!IsChanged) return _lastImage; var lCenter = 0; var rCenter = 0; Image lImg = null, rImg = null; if(Left != null) lImg = Left.Draw(out lCenter); if(Right != null) rImg = Right.Draw(out rCenter); var me = new Bitmap(40, 40); var g = Graphics.FromImage(me); g.SmoothingMode = SmoothingMode.HighQuality; var rcl = new Rectangle(0, 0, me.Width - 1, me.Height - 1); g.FillRectangle(Brushes.White, rcl); g.FillEllipse(new LinearGradientBrush(new Point(0, 0), new Point(me.Width, me.Height), Color.Gold, Color.Black), rcl); var lSize = new Size(); var rSize = new Size(); var under = (lImg != null) || (rImg != null); if(lImg != null) lSize = lImg.Size; if(rImg != null) rSize = rImg.Size; var maxHeight = lSize.Height; if(maxHeight < rSize.Height) maxHeight = rSize.Height; var resSize = new Size { Width = me.Size.Width + lSize.Width + rSize.Width, Height = me.Size.Height + (under ? maxHeight + me.Size.Height : 0) }; var result = new Bitmap(resSize.Width, resSize.Height); g = Graphics.FromImage(result); g.SmoothingMode = SmoothingMode.HighQuality; g.FillRectangle(Brushes.White, new Rectangle(new Point(0, 0), resSize)); g.DrawImage(me, lSize.Width, 0); g.DrawString(Value.ToString(), new Font("Tahoma", 14), Brushes.White, lSize.Width + 5, me.Height / 2f - 12); center = lSize.Width + me.Width / 2; var pen = new Pen(Brushes.Black, 2.5f) { EndCap = LineCap.ArrowAnchor, StartCap = LineCap.Round }; float x1 = center; float y1 = me.Height; float y2 = me.Height * 2; float x2 = lCenter; var h = Math.Abs(y2 - y1); var w = Math.Abs(x2 - x1); if(lImg != null) { g.DrawImage(lImg, 0, me.Size.Height * 2); var points1 = new List { new PointF(x1, y1), new PointF(x1 - w/6, y1 + h/3.5f), new PointF(x2 + w/6, y2 - h/3.5f), new PointF(x2, y2), }; g.DrawCurve(pen, points1.ToArray(), 0.5f); } if(rImg != null) { g.DrawImage(rImg, lSize.Width + me.Size.Width, me.Size.Height * 2); x2 = rCenter + lSize.Width + me.Width; w = Math.Abs(x2 - x1); var points = new List { new PointF(x1, y1), new PointF(x1 + w/6, y1 + h/3.5f), new PointF(x2 - w/6, y2 - h/3.5f), new PointF(x2, y2) }; g.DrawCurve(pen, points.ToArray(), 0.5f); } IsChanged = false; _lastImage = result; _lastCenter = center; return result; } class BinaryTree { public Node RootNode { get; private set; } public BinaryTree() : this(-1) { } public BinaryTree(int rootValue) { RootNode = new Node(-1); } public List Items { get; private set; } public void Add(int value) { RootNode.Add(value); } public bool Remove(int value) { bool isRootNode; var res = RootNode.Remove(value, out isRootNode); return !isRootNode && res; } public Image Draw() { int temp; return RootNode.Right == null ? null : RootNode.Right.Draw(out temp); } }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485