如何使用带括号的字符串创建二叉树? 可能的解决方案还会以某种方式利用堆栈吗?
示例:字符串:"(A(B)(C))"
将创建一棵如下所示的树:
A
/ \
B C
还有一点要注意的是,()
将用于表示节点在该位置将没有子节点。为了进一步说明,"(A()(B))"
,
表示节点“ A”将没有左子节点,但将有一个右子节点,名为“ B”。
答案 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
答案 1 :(得分:0)
一种实现方法是在Parse
类上创建静态Node
方法,该方法根据输入字符串返回Node
。逻辑将类似于:
Node.Parse
的结果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();
}
输出看起来像: