C#在Console中显示二进制搜索树

时间:2016-03-30 14:32:50

标签: c# console binary-tree

我有简单的二叉搜索树

public class BNode
{
    public int item;
    public BNode right;
    public BNode left;

    public BNode(int item)
    {
        this.item = item;
    }
}

public class BTree
{
    private BNode _root;
    private int _count;
    private IComparer<int> _comparer = Comparer<int>.Default;


    public BTree()
    {
        _root = null;
        _count = 0;
    }


    public bool Add(int Item)
    {
        if (_root == null)
        {
            _root = new BNode(Item);
            _count++;
            return true;
        }
        else
        {
            return Add_Sub(_root, Item);
        }
    }

    private bool Add_Sub(BNode Node, int Item)
    {
        if (_comparer.Compare(Node.item, Item) < 0)
        {
            if (Node.right == null)
            {
                Node.right = new BNode(Item);
                _count++;
                return true;
            }
            else
            {
                return Add_Sub(Node.right, Item);
            }
        }
        else if (_comparer.Compare(Node.item, Item) > 0)
        {
            if (Node.left == null)
            {
                Node.left = new BNode(Item);
                _count++;
                return true;
            }
            else
            {
                return Add_Sub(Node.left, Item);
            }
        }
        else
        {
            return false;
        }
    }

    public void Print()
    {
        Print(_root, 4);
    }

    public void Print(BNode p, int padding)
    {
        if (p != null)
        {
            if (p.right != null)
            {
                Print(p.right, padding + 4);
            }
            if (padding > 0)
            {
                Console.Write(" ".PadLeft(padding));
            }
            if (p.right != null)
            {
                Console.Write("/\n");
                Console.Write(" ".PadLeft(padding));
            }
            Console.Write(p.item.ToString() + "\n ");
            if (p.left != null)
            {
                Console.Write(" ".PadLeft(padding) + "\\\n");
                Print(p.left, padding + 4);
            }
        }
    }
}

我可以插入像

这样的值
BTree btr = new BTree();
btr.Add(6);
btr.Add(2);
btr.Add(3);
btr.Add(11);
btr.Add(30);
btr.Add(9);
btr.Add(13);
btr.Add(18);

我想在控制台应用程序中显示我的树。我有一个btr.Print();从左到右显示我的树(6是根) - 但我对它不满意。

enter image description here

问题:有没有更好的方法在控制台应用程序中显示此树?即使对此Print()的改进也会对我有所帮助。

3 个答案:

答案 0 :(得分:26)

我最终得到了以下允许您打印任意子树的方法:

public static class BTreePrinter
{
    class NodeInfo
    {
        public BNode Node;
        public string Text;
        public int StartPos;
        public int Size { get { return Text.Length; } }
        public int EndPos { get { return StartPos + Size; } set { StartPos = value - Size; } }
        public NodeInfo Parent, Left, Right;
    }

    public static void Print(this BNode root, string textFormat = "0", int spacing = 1, int topMargin = 2, int leftMargin = 2)
    {
        if (root == null) return;
        int rootTop = Console.CursorTop + topMargin;
        var last = new List<NodeInfo>();
        var next = root;
        for (int level = 0; next != null; level++)
        {
            var item = new NodeInfo { Node = next, Text = next.item.ToString(textFormat) };
            if (level < last.Count)
            {
                item.StartPos = last[level].EndPos + spacing;
                last[level] = item;
            }
            else
            {
                item.StartPos = leftMargin;
                last.Add(item);
            }
            if (level > 0)
            {
                item.Parent = last[level - 1];
                if (next == item.Parent.Node.left)
                {
                    item.Parent.Left = item;
                    item.EndPos = Math.Max(item.EndPos, item.Parent.StartPos - 1);
                }
                else
                {
                    item.Parent.Right = item;
                    item.StartPos = Math.Max(item.StartPos, item.Parent.EndPos + 1);
                }
            }
            next = next.left ?? next.right;
            for (; next == null; item = item.Parent)
            {
                int top = rootTop + 2 * level;
                Print(item.Text, top, item.StartPos);
                if (item.Left != null)
                {
                    Print("/", top + 1, item.Left.EndPos);
                    Print("_", top, item.Left.EndPos + 1, item.StartPos);
                }
                if (item.Right != null)
                {
                    Print("_", top, item.EndPos, item.Right.StartPos - 1);
                    Print("\\", top + 1, item.Right.StartPos - 1);
                }
                if (--level < 0) break;
                if (item == item.Parent.Left)
                {
                    item.Parent.StartPos = item.EndPos + 1;
                    next = item.Parent.Node.right;
                }
                else
                {
                    if (item.Parent.Left == null)
                        item.Parent.EndPos = item.StartPos - 1;
                    else
                        item.Parent.StartPos += (item.StartPos - 1 - item.Parent.EndPos) / 2;
                }
            }
        }
        Console.SetCursorPosition(0, rootTop + 2 * last.Count - 1);
    }

    private static void Print(string s, int top, int left, int right = -1)
    {
        Console.SetCursorPosition(left, top);
        if (right < 0) right = left + s.Length;
        while (Console.CursorLeft < right) Console.Write(s);
    }
}

如您所见,我添加了一些影响格式化的参数。默认情况下,它会生成最紧凑的表示。

为了使用它,我修改了BTree类,如下所示:

public class BTree
{
    // ...

    public BNode Root { get { return _root; } }

    public void Print()
    {
        Root.Print();
    }
}

使用您的样本数据,以下是一些结果:

btr.Root.Print();

enter image description here

btr.Root.Print(textFormat: "(0)", spacing: 2);

enter image description here

更新: IMO上面的默认格式是紧凑且可读的,但为了好玩,调整算法以产生更多“图形”输出(textFormatspacing参数移除):

public static class BTreePrinter
{
    class NodeInfo
    {
        public BNode Node;
        public string Text;
        public int StartPos;
        public int Size { get { return Text.Length; } }
        public int EndPos { get { return StartPos + Size; } set { StartPos = value - Size; } }
        public NodeInfo Parent, Left, Right;
    }

    public static void Print(this BNode root, int topMargin = 2, int leftMargin = 2)
    {
        if (root == null) return;
        int rootTop = Console.CursorTop + topMargin;
        var last = new List<NodeInfo>();
        var next = root;
        for (int level = 0; next != null; level++)
        {
            var item = new NodeInfo { Node = next, Text = next.item.ToString(" 0 ") };
            if (level < last.Count)
            {
                item.StartPos = last[level].EndPos + 1;
                last[level] = item;
            }
            else
            {
                item.StartPos = leftMargin;
                last.Add(item);
            }
            if (level > 0)
            {
                item.Parent = last[level - 1];
                if (next == item.Parent.Node.left)
                {
                    item.Parent.Left = item;
                    item.EndPos = Math.Max(item.EndPos, item.Parent.StartPos);
                }
                else
                {
                    item.Parent.Right = item;
                    item.StartPos = Math.Max(item.StartPos, item.Parent.EndPos);
                }
            }
            next = next.left ?? next.right;
            for (; next == null; item = item.Parent)
            {
                Print(item, rootTop + 2 * level);
                if (--level < 0) break;
                if (item == item.Parent.Left)
                {
                    item.Parent.StartPos = item.EndPos;
                    next = item.Parent.Node.right;
                }
                else
                {
                    if (item.Parent.Left == null)
                        item.Parent.EndPos = item.StartPos;
                    else
                        item.Parent.StartPos += (item.StartPos - item.Parent.EndPos) / 2;
                }
            }
        }
        Console.SetCursorPosition(0, rootTop + 2 * last.Count - 1);
    }

    private static void Print(NodeInfo item, int top)
    {
        SwapColors();
        Print(item.Text, top, item.StartPos);
        SwapColors();
        if (item.Left != null)
            PrintLink(top + 1, "┌", "┘", item.Left.StartPos + item.Left.Size / 2, item.StartPos);
        if (item.Right != null)
            PrintLink(top + 1, "└", "┐", item.EndPos - 1, item.Right.StartPos + item.Right.Size / 2);
    }

    private static void PrintLink(int top, string start, string end, int startPos, int endPos)
    {
        Print(start, top, startPos);
        Print("─", top, startPos + 1, endPos);
        Print(end, top, endPos);
    }

    private static void Print(string s, int top, int left, int right = -1)
    {
        Console.SetCursorPosition(left, top);
        if (right < 0) right = left + s.Length;
        while (Console.CursorLeft < right) Console.Write(s);
    }

    private static void SwapColors()
    {
        var color = Console.ForegroundColor;
        Console.ForegroundColor = Console.BackgroundColor;
        Console.BackgroundColor = color;
    }
}

结果是:

enter image description here

答案 1 :(得分:9)

这是我的看法:

我已将PrintPretty添加到BNode,我删除了您在BTree中的第二个Print功能。

(编辑:我通过更改原始字符来使树更加可见,以绘制树的分支)

    static void Main(string[] args)
    {
        BTree btr = new BTree();
        btr.Add(6);
        btr.Add(2);
        btr.Add(3);
        btr.Add(11);
        btr.Add(30);
        btr.Add(9);
        btr.Add(13);
        btr.Add(18);

        btr.Print();

    }

    public class BNode
    {
        public int item;
        public BNode right;
        public BNode left;

        public BNode(int item)
        {
            this.item = item;
        }

        public void PrintPretty(string indent, bool last)
        {

            Console.Write(indent);
            if (last)
            {
                Console.Write("└─");
                indent += "  ";
            }
            else
            {
                Console.Write("├─");
                indent += "| ";
            }
            Console.WriteLine(item);

            var children = new List<BNode>();
            if (this.left != null)
                children.Add(this.left);
            if (this.right != null)
                children.Add(this.right);

            for (int i = 0; i < children.Count; i++)
                children[i].PrintPretty(indent, i == children.Count - 1);

        }

    }

    public class BTree
    {
        private BNode _root;
        private int _count;
        private IComparer<int> _comparer = Comparer<int>.Default;


        public BTree()
        {
            _root = null;
            _count = 0;
        }


        public bool Add(int Item)
        {
            if (_root == null)
            {
                _root = new BNode(Item);
                _count++;
                return true;
            }
            else
            {
                return Add_Sub(_root, Item);
            }
        }

        private bool Add_Sub(BNode Node, int Item)
        {
            if (_comparer.Compare(Node.item, Item) < 0)
            {
                if (Node.right == null)
                {
                    Node.right = new BNode(Item);
                    _count++;
                    return true;
                }
                else
                {
                    return Add_Sub(Node.right, Item);
                }
            }
            else if (_comparer.Compare(Node.item, Item) > 0)
            {
                if (Node.left == null)
                {
                    Node.left = new BNode(Item);
                    _count++;
                    return true;
                }
                else
                {
                    return Add_Sub(Node.left, Item);
                }
            }
            else
            {
                return false;
            }
        }

        public void Print()
        {
            _root.PrintPretty("", true);
        }

    }

这是结果(如我所提到的那样更紧凑):

enter image description here

修改:为了显示左右信息,修改了以下代码:

    static void Main(string[] args)
    {
        BTree btr = new BTree();
        btr.Add(6);
        btr.Add(2);
        btr.Add(3);
        btr.Add(11);
        btr.Add(30);
        btr.Add(9);
        btr.Add(13);
        btr.Add(18);

        btr.Print();

    }

    public enum NodePosition
    {
        left,
        right,
        center
    }

    public class BNode
    {
        public int item;
        public BNode right;
        public BNode left;

        public BNode(int item)
        {
            this.item = item;
        }

        private void PrintValue(string value, NodePosition nodePostion)
        {
            switch (nodePostion)
            {
                case NodePosition.left:
                    PrintLeftValue(value);
                    break;
                case NodePosition.right:
                    PrintRightValue(value);
                    break;
                case NodePosition.center:
                    Console.WriteLine(value);
                    break;
                default:
                    throw new NotImplementedException();
            }
        }

        private void PrintLeftValue(string value)
        {
            Console.ForegroundColor = ConsoleColor.Magenta;
            Console.Write("L:");
            Console.ForegroundColor = (value == "-") ? ConsoleColor.Red : ConsoleColor.Gray;
            Console.WriteLine(value);
            Console.ForegroundColor = ConsoleColor.Gray;
        }

        private void PrintRightValue(string value)
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.Write("R:");
            Console.ForegroundColor = (value == "-") ? ConsoleColor.Red : ConsoleColor.Gray;
            Console.WriteLine(value);
            Console.ForegroundColor = ConsoleColor.Gray;
        }

        public void PrintPretty(string indent, NodePosition nodePosition, bool last, bool empty)
        {

            Console.Write(indent);
            if (last)
            {
                Console.Write("└─");
                indent += "  ";
            }
            else
            {
                Console.Write("├─");
                indent += "| ";
            }

            var stringValue = empty ? "-" : item.ToString();
            PrintValue(stringValue, nodePosition);

            if(!empty && (this.left != null || this.right != null))
            {
                if (this.left != null)
                    this.left.PrintPretty(indent, NodePosition.left, false, false);
                else
                    PrintPretty(indent, NodePosition.left, false, true);

                if (this.right != null)
                    this.right.PrintPretty(indent, NodePosition.right, true, false);
                else
                    PrintPretty(indent, NodePosition.right, true, true);
            }
        }

    }

    public class BTree
    {
        private BNode _root;
        private int _count;
        private IComparer<int> _comparer = Comparer<int>.Default;


        public BTree()
        {
            _root = null;
            _count = 0;
        }


        public bool Add(int Item)
        {
            if (_root == null)
            {
                _root = new BNode(Item);
                _count++;
                return true;
            }
            else
            {
                return Add_Sub(_root, Item);
            }
        }

        private bool Add_Sub(BNode Node, int Item)
        {
            if (_comparer.Compare(Node.item, Item) < 0)
            {
                if (Node.right == null)
                {
                    Node.right = new BNode(Item);
                    _count++;
                    return true;
                }
                else
                {
                    return Add_Sub(Node.right, Item);
                }
            }
            else if (_comparer.Compare(Node.item, Item) > 0)
            {
                if (Node.left == null)
                {
                    Node.left = new BNode(Item);
                    _count++;
                    return true;
                }
                else
                {
                    return Add_Sub(Node.left, Item);
                }
            }
            else
            {
                return false;
            }
        }

        public void Print()
        {
            _root.PrintPretty("", NodePosition.center, true, false);
        }

    }

结果:

enter image description here

答案 2 :(得分:1)

如果你的树是 QuickGraph IBidirectionalGraph 的形式,你可以像这样递归地打印它:

static void PrintVertex<TVertex>(IBidirectionalGraph<TVertex, IEdge<TVertex>> graph, TVertex vertex, Func<TVertex, string> labelSelector, bool[] isLastVertexIntendations = null)
{
    var prefix = string.Concat(Enumerable.Concat(
        /*up to last*/isLastVertexIntendations.NeverNull().Reverse().Skip(1).Reverse().Select(isLast => isLast ? "  " : "│ "),
        /*last*/isLastVertexIntendations.NeverNull().Reverse().Take(1).Select(isLast => isLast ? "└─" : "├─")
    ));
    Console.Write(prefix);
    Console.WriteLine(labelSelector(vertex));
    var edges = graph.OutEdges(vertex);
    foreach (var edge in edges)
    {
        PrintVertex(graph, edge.Target, labelSelector, isLastVertexIntendations: isLastVertexIntendations.NeverNull().Concat(new[] { edge == edges.Last() }).ToArray());
    }
}

enter image description here