从带括号的字符串创建二叉树

时间:2018-11-06 21:58:39

标签: c# string tree binary

如何使用带括号的字符串创建二叉树? 可能的解决方案还会以某种方式利用堆栈吗?

示例:字符串:"(A(B)(C))"将创建一棵如下所示的树:

  A
 / \
B   C

还有一点要注意的是,()将用于表示节点在该位置将没有子节点。为了进一步说明,"(A()(B))", 表示节点“ A”将没有左子节点,但将有一个右子节点,名为“ B”。

2 个答案:

答案 0 :(得分:0)

假设您输入的字符串格式正确,则实际上可以使用堆栈来构建树结构。我使用了一堆标记,这些标记与一堆父节点并排运行,以跟踪是否已处理了每个父节点的左子节点(保留为空或分配了子节点)。还修改了此标志堆栈,以表示空子代的空括号。

using System;
using System.Collections.Generic;

class MainClass {
    public static void Main(string[] args) {
        PrintTree(ParseTree("(A(B(E)(F))(C(G)(H)))"), 0);
    }

    static Node ParseTree(string str) {
        var parent = new Stack<Node>();
        var hasLeft = new Stack<bool>();
        Node root = null;

        for (int i = 0; i < str.Length; i++) {
            if (str[i] == '(') {
                if (str[++i] == ')') {
                    hasLeft.Pop();
                    hasLeft.Push(true);
                }
                else {
                    var node = new Node(str[i]);

                    if (parent.Count > 0) {
                        if (hasLeft.Peek()) {
                            parent.Peek().right = node;
                        }
                        else {
                            parent.Peek().left = node;
                            hasLeft.Pop();
                            hasLeft.Push(true);
                        }
                    }
                    else {
                        root = node;
                    }

                    parent.Push(node);
                    hasLeft.Push(false);
                }
            }
            else if (str[i] == ')') {
                parent.Pop();
                hasLeft.Pop();
            }
        }

        return root;
    }

    static void PrintTree(Node root, int depth) {
        if (root != null) {
            Console.WriteLine(new String(' ', depth) + root.val);

            if (root.left != null || root.right != null) {
                Console.WriteLine(new String(' ', depth) +  "────┐");
            }

            PrintTree(root.left, depth + 4);
            PrintTree(root.right, depth + 4);
        }
    }
}

class Node {
    public Node left;
    public Node right;
    public char val;

    public Node(char val) {
        this.val = val;
    }
}

输出:

A
────┐
    B
    ────┐
        E
        F
    C
    ────┐
        G
        H

Try it!

答案 1 :(得分:0)

一种实现方法是在Parse类上创建静态Node方法,该方法根据输入字符串返回Node。逻辑将类似于:

  1. 删除外部括号
  2. 删除名称并将其分配给新节点
  3. 确定代表左节点的字符串(这可以通过计算左括号和右括号直到每个的计数相等来完成)。
  4. 使用“左”字符串将“左”节点分配给Node.Parse的结果
  5. 使用“右”字符串将“右”节点分配给Node.Parse的结果

我还添加了一些代码,以一种基本的方式打印出树,因此您可以看到每个节点的值。

例如,下面的Node类实现了一个Parse方法,该方法将根据您的字符串输入创建节点:

public class Node
{
    public string Name { get; set; }
    public Node Left { get; set; }
    public Node Right { get; set; }

    /// <summary>
    /// Creates a Node based on a valid input string: "(Name(Left)(Right))", 
    /// where 'Left' and 'Right' are empty or valid strings like above.
    /// </summary>
    /// <param name="input">Input string to parse</param>
    /// <returns>Root node of the tree</returns>
    public static Node Parse(string input)
    {
        input = input?.Trim();

        // Some light validation
        if (string.IsNullOrEmpty(input) || input == "()") return null;

        if (input.Length < 7)
        {
            throw new ArgumentException(
                $"input '{input}' is not long enough to represent a valid " +
                "node. The minimum string length is 7 characters: (A()())");
        }

        if (!input.StartsWith("(") || !input.EndsWith(")"))
        {
            throw new FormatException(
                $"input '{input}' must be surrounded by parenthesis");
        }

        // Remove outer parenthesis so input is now: "Name(Left)(Right)"
        input = input.Substring(1, input.Length - 2);

        // Find the name and start of left node
        var leftNodeStart = input.IndexOf('(', 1);
        var root = new Node {Name = input.Substring(0, leftNodeStart)};

        // Remove name so input is now just: "(Left)(Right)"
        input = input.Substring(leftNodeStart);

        // Find the end of the left node by counting opening and closing parenthesis
        // When the opening count is equal to the closing count, we have our Left set
        var openParenthesisCount = 0;
        var closeParenthesisCount = 0;
        var leftNodeLength = 0;

        for (int i = 0; i < input.Length - 1; i++)
        {
            if (input[i] == '(') openParenthesisCount++;
            else if (input[i] == ')') closeParenthesisCount++;

            if (openParenthesisCount == closeParenthesisCount)
            {
                leftNodeLength = i + 1;
                break;
            }
        }

        // Recursive calls to create Left and Right children
        root.Left = Node.Parse(input.Substring(0, leftNodeLength));
        root.Right = Node.Parse(input.Substring(leftNodeLength));

        return root;
    }

    public void Print()
    {
        PrintTree();
    }        

    private static class Connector
    {
        public const string Empty = "  ";
        public const string Single = "╚═";
        public const string Double = "╠═";
        public const string Extension = "║";
    }

    private static class Position
    {
        public const string Empty = "";
        public const string Left = "Left : ";
        public const string Right = "Right: ";
    }

    private void PrintTree(string indent = "", string position = Position.Empty,
        bool extendParentConnector = false, string connector = Connector.Empty)
    {
        // Extend the parent's connector if necessary by
        // adding a "║" under the parent node's connector
        if (extendParentConnector && indent.Length > position.Length + 2)
        {
            var replaceIndex = indent.Length - position.Length - 2;
            indent = indent.Remove(replaceIndex, 1)
                .Insert(replaceIndex, Connector.Extension);
        }
        Console.ForegroundColor = ConsoleColor.DarkMagenta;
        Console.Write(indent + connector);

        Console.ForegroundColor = position == Position.Left
            ? ConsoleColor.DarkCyan
            : ConsoleColor.DarkYellow;

        Console.Write(position);
        Console.BackgroundColor = ConsoleColor.DarkGray;
        Console.ForegroundColor = ConsoleColor.DarkBlue;
        Console.WriteLine($" {Name} ");
        Console.ResetColor();

        var nextIndent = indent + new string(' ', position.Length + 2);
        var extendParent = connector == Connector.Double;

        Left?.PrintTree(nextIndent, Position.Left, extendParent,
            Right == null ? Connector.Single : Connector.Double);

        Right?.PrintTree(nextIndent, Position.Right, extendParent, Connector.Single);
    }
}   

在使用中,它看起来像:

static void Main(string[] args)
{
    var nodeString = "(A(B(C(L()())(M()()))(D()()))(E(F(G(H()())())(I()(J()())))(K()())))";

    var rootNode = Node.Parse(nodeString);

    rootNode.Print();

    Console.Write("\nDone! Press any key to exit...");
    Console.ReadKey();
}

输出看起来像:

![![enter image description here