二叉树的文本显示

引言

在用二叉树编程时,总希望看到二叉树的内部结构。使用各种遍历方法虽能将二叉树按某种顺序转换成一个字符串,但不太直观。如要绘成图形,又将增加不少资源开销。如能将二叉树用文本方法显示其树状结构,则既节约开销,又不失直观。本文在VS2010以WPF框架实现

基本思路

要构建二叉树,首先得建立一个二叉树节点的类,如BinNode,该类具有二叉树的基本属性:Text(节点文本)、Father(父节点)、Laft(左节点)、Right(右节点)等。为了确定一节点在树中的位置,我们为该类添加了Level(节点所在层,根节点的Level=0)、Index(节点在完全二叉树中按层次遍历法的顺序)。
有了BinNode,即可构造一二叉树,记住其根节点(设为Root)
第三步,将所构造的二叉树使用层次遍历法,构造出文本形式的树状结构。

构造节点类

在拟建项目中添加类 BinNode,代码如下:

public class BinNode
    {
        #region 属性
        
        public string Text { get; set; }
        public int Value { get; set; }
        public int Level { get; set; }
        public int Index { get; set; }
        public bool? IsLeft { get; set; }
        public BinNode Left { get; set; }
        public BinNode Right { get; set; }
        public BinNode Father { get; set; }
        public int Count
        {
            get
            {
                return countNode(this);
            }
        }
        public int Height
        {
            get
            {
                return getHeight(this);
            }
        }
        public int Priority
        {
            get
            {
                if ("*/".Contains(this.Text)) return 2;
                else if ("+-".Contains(this.Text)) return 1;
                else return 0;
            }
        }
        public bool IsRight
        {
            get
            {
                return this.Father != null && IsLeft == false;
            }
        }
        public bool IsOperator
        {
            get
            {
                return "+-*/".Contains(this.Text);
            }
        }
        public bool IsNumber
        {
            get
            {
                return char.IsDigit(this.Text[0]);
            }
        }

        #endregion

        public BinNodei(string text)
        {
            this.Text = text;
            this.Level = 0;
            this.Index = 0;
        }

        public BinNode(string text, BinNodei father, bool? isLeft)
        {
            this.Text = text;
            this.Father = father;
            this.IsLeft = isLeft;
            this.Level = father.Level + 1;
            if (father == null) this.Index = 0;
            else
            {
                if (isLeft == true) this.Index = 2 * father.Index + 1;
                else this.Index = 2 * father.Index + 2;
            }
        }

        private int countNode(BinNode node)
        {
            if (node == null) return 0;
            int nl = countNode(node.Left);
            int nr = countNode(node.Right);
            return 1 + nl + nr;
        }

        private int getHeight(BinNode node)
        {
            if (node == null) return 0;
            if (node.Left == null && node.Right == null) return 1;
            int lh = 0, rh = 0;
            lh = getHeight(node.Left);
            rh = getHeight(node.Right);
            return (lh > rh ? lh : rh) + 1;
        }
    }

添加测试窗口

在项目中添加窗口WinTest,窗口有三行,依次是工具栏,文本框(供输出树状图),状态栏,工具栏中有一文本框用于输入要创建二叉树的节点数,和三个按钮“构造”,“显示”,“清空”。效果如图:
测试窗口效果图

	<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <ToolBar>
            <Label>节点数:</Label>
            <TextBox Name="tbCount" Text="5"/>
            <Separator />
            <Button Name="btBuild" Click="btBuild_Click">构造</Button>
            <Button Name="btShow" Click="btShow_Click">显示</Button>
            <Button Name="btCls" Click="btCls_Click">清空</Button>
        </ToolBar>
        <TextBox Name="tbOut" Grid.Row="1" TextWrapping="NoWrap" 
                 HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" FontFamily="NSimSun" />
        <StatusBar Grid.Row="2">
            <TextBlock Name="tbStatus"/>
        </StatusBar>
    </Grid>

需要说明的是,为了使节点显示在正确的文本位置,中间的文本框的字体需设置等宽字体

编写后台代码

转到测试窗口的代码页,开始编写实现代码。

1. 构建二叉树

这里我们以“根据给定节点数构建二叉树”为例,节点文本为随机的大写字母。新建二叉树函数如下:

	/// <summary>
    /// 根据节点数递归构造二叉树
    /// </summary>
    /// <param name="node">当前节点</param>
    /// <param name="n">剩余节点数</param>
    void newTree(BinNode node, int n)
    {
        if (n < 1) return;
        string txt = rndChar();
        if (n == 1)
        {
            int t = rand.Next(0, 10);
            if (t < 5) node.Left = new BinNode(txt, node, true);
            else node.Right = new BinNode(txt, node, false);
            return;
        }
        
        node.Left = new BinNode(txt, node, true);
        txt = rndChar();
        node.Right = new BinNode(txt, node, false);
        int n1 = (n - 2) / 2;
        newTree(node.Left, n1);
        newTree(node.Right, n - n1 - 2);
    }    

代码采用递归算法,先构造当前节点的左、右子节点,再以刚建立的左、右子节点为参数递归调用新建函数。其中用到的rndChar()函数,返回一个随机的大写英文字母,函数体内仅一个语句:

return new string((char)rand.Next(65, 91), 1);

2. 构造文本形式的树状图

用层次遍历法遍历二叉树,遍历到新层时,计算当前节点与前一节点的空格数,确定层中每一节点的位置,构建每一层的文本,再添加到总的文本中。代码为:

//层次遍历二叉树,构造文本形式的树状图
        private string lev(BinNode node)
        {
            if (node == null) return "";
            int lev = 0;
            int h = node.Height;
            int nn = (int)Math.Pow(2, h - 1);   //最后一行节点数的最大值
            int ns = 2 * nn - 1;                //最后一行间隔数的最大值
            double space = (nn + ns);           //间隔大小

            StringBuilder sb = new StringBuilder();//总字符串
            StringBuilder line = new StringBuilder();//当前行字符串
            Queue<BinNodei> q = new Queue<BinNodei>();
            q.Enqueue(node);
            while (q.Count > 0)
            {
                BinNodei n = q.Dequeue();
                if (n.Level > lev)
                {
                    sb.AppendLine(line.ToString());
                    line = new StringBuilder();
                    lev = n.Level;
                    space /= 2;
                }
                int sp = (int)Math.Ceiling(space);
                if (n.Level > 0)
                {
                    int first = ((int)Math.Pow(2, n.Level) - 1);
                    int diff = n.Index - first;
                    sp = (int)Math.Ceiling(space * (2 * diff + 1) - line.Length);
                }

                line.AppendFormat("{0," + sp + "}", n.Text);

                if (n.Left != null) q.Enqueue(n.Left);
                if (n.Right != null) q.Enqueue(n.Right);
            }
            sb.AppendLine(line.ToString());
            return sb.ToString();
        }

3. 按钮单击事件处理函数

在“构造”、“显示”按钮单击事件处理函数中分别调用newTree和lev函数即可。在“构造”按钮单击事件处理函数中的代码是:

	//“构造”按钮单击事件处理函数
	private void btBuild_Click(object sender, RoutedEventArgs e)
   {
       int n = Convert.ToInt16(tbCount.Text);
       string text = new string((char)rand.Next(65, 91), 1);
       root = new BinNode(text);
       newTree(root, n - 1);
       tbStatus.Text = string.Format("构造成功!二叉树有{0}层,{1}个节点", root.Height, root.Count);
   }
	//“显示”按钮单击事件处理函数
   private void btShow_Click(object sender, RoutedEventArgs e)
   {
       string tree = lev(root);
       tbOut.AppendText(tree+"\n");
   }
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值