自定义树问题
您将获得一组链接,例如
a ---> b
b ---> c
b ---> d
a ---> e
打印当每对具有与起点和终点相同的字符的链接连接在一起时将形成的树。您必须保持节点高度的保真度,即距根高度为 n 的节点应打印在同一行或同一列。对于上面给出的一组链接,打印的树应该是 -
-->a
|-->b
| |-->c
| |-->d
|-->e
请注意,这些链接不需要形成一棵树;他们可以形成,咳咳,一片森林。考虑以下链接
a ---> b
a ---> g
b ---> c
c ---> d
d ---> e
c ---> f
z ---> y
y ---> x
x ---> w
输出将跟随森林。
-->a
|-->b
| |-->c
| | |-->d
| | | |-->e
| | |-->f
|-->g
-->z
|-->y
| |-->x
| | |-->w
您可以假设给定的链接只能形成一棵树或森林,并且链接之间没有重复。
解决方案:想法是维护两个数组,一个数组用于树节点,另一个用于树本身(我们称之为数组森林)。节点数组的元素包含对应于各个字符的 TreeNode 对象。森林数组的一个元素包含对应于各个树根的 Tree 对象。
很明显,关键部分是在这里创建森林,一旦创建,以所需格式打印出来很简单。要创建森林,请使用以下过程 -
Do following for each input link,
1. If start of link is not present in node array
Create TreeNode objects for start character
Add entries of start in both arrays.
2. If end of link is not present in node array
Create TreeNode objects for start character
Add entry of end in node array.
3. If end of link is present in node array.
If end of link is present in forest array, then remove it
from there.
4. Add an edge (in tree) between start and end nodes of link.
应该清楚的是,这个过程在节点数和链接数上以线性时间运行——它只通过链接一次。就字母大小而言,它还需要线性空间。
以下是上述算法的Java实现。在以下实现中,假设字符仅是从“a”到“z”的小写字符。
Java
// Java program to create a custom tree from a given set of links.
// The main class that represents tree and has main method
public class Tree {
private TreeNode root;
/* Returns an array of trees from links input. Links are assumed to
be Strings of the form " " where and are starting
and ending points for the link. The returned array is of size 26
and has non-null values at indexes corresponding to roots of trees
in output */
public Tree[] buildFromLinks(String [] links) {
// Create two arrays for nodes and forest
TreeNode[] nodes = new TreeNode[26];
Tree[] forest = new Tree[26];
// Process each link
for (String link : links) {
// Find the two ends of current link
String[] ends = link.split(" ");
int start = (int) (ends[0].charAt(0) - 'a'); // Start node
int end = (int) (ends[1].charAt(0) - 'a'); // End node
// If start of link not seen before, add it two both arrays
if (nodes[start] == null)
{
nodes[start] = new TreeNode((char) (start + 'a'));
// Note that it may be removed later when this character is
// last character of a link. For example, let we first see
// a--->b, then c--->a. We first add 'a' to array of trees
// and when we see link c--->a, we remove it from trees array.
forest[start] = new Tree(nodes[start]);
}
// If end of link is not seen before, add it to the nodes array
if (nodes[end] == null)
nodes[end] = new TreeNode((char) (end + 'a'));
// If end of link is seen before, remove it from forest if
// it exists there.
else forest[end] = null;
// Establish Parent-Child Relationship between Start and End
nodes[start].addChild(nodes[end], end);
}
return forest;
}
// Constructor
public Tree(TreeNode root) { this.root = root; }
public static void printForest(String[] links)
{
Tree t = new Tree(new TreeNode('\0'));
for (Tree t1 : t.buildFromLinks(links)) {
if (t1 != null)
{
t1.root.printTreeIdented("");
System.out.println("");
}
}
}
// Driver method to test
public static void main(String[] args) {
String [] links1 = {"a b", "b c", "b d", "a e"};
System.out.println("------------ Forest 1 ----------------");
printForest(links1);
String [] links2 = {"a b", "a g", "b c", "c d", "d e", "c f",
"z y", "y x", "x w"};
System.out.println("------------ Forest 2 ----------------");
printForest(links2);
}
}
// Class to represent a tree node
class TreeNode {
TreeNode []children;
char c;
// Adds a child 'n' to this node
public void addChild(TreeNode n, int index) { this.children[index] = n;}
// Constructor
public TreeNode(char c) { this.c = c; this.children = new TreeNode[26];}
// Recursive method to print indented tree rooted with this node.
public void printTreeIdented(String indent) {
System.out.println(indent + "-->" + c);
for (TreeNode child : children) {
if (child != null)
child.printTreeIdented(indent + " |");
}
}
}
C#
// C# program to create a custom tree
// from a given set of links.
using System;
// The main class that represents tree
// and has main method
class Tree
{
public TreeNode root;
/* Returns an array of trees from links input.
Links are assumed to be Strings of the form
" " where and are starting and
ending points for the link. The returned array is
of size 26 and has non-null values at indexes
corresponding to roots of trees in output */
public Tree[] buildFromLinks(String [] links)
{
// Create two arrays for nodes and forest
TreeNode[] nodes = new TreeNode[26];
Tree[] forest = new Tree[26];
// Process each link
foreach (String link in links)
{
char []sep = {' ',' '};
// Find the two ends of current link
String[] ends = link.Split(sep);
int start = (int) (ends[0][0] - 'a'); // Start node
int end = (int) (ends[1][0] - 'a'); // End node
// If start of link not seen before,
// add it two both arrays
if (nodes[start] == null)
{
nodes[start] = new TreeNode((char) (start + 'a'));
// Note that it may be removed later when
// this character is last character of a link.
// For example, let we first see a--->b,
// then c--->a. We first add 'a' to array
// of trees and when we see link c--->a,
// we remove it from trees array.
forest[start] = new Tree(nodes[start]);
}
// If end of link is not seen before,
// add it to the nodes array
if (nodes[end] == null)
nodes[end] = new TreeNode((char) (end + 'a'));
// If end of link is seen before,
// remove it from forest if it exists there.
else forest[end] = null;
// Establish Parent-Child Relationship
// between Start and End
nodes[start].addChild(nodes[end], end);
}
return forest;
}
// Constructor
public Tree(TreeNode root) { this.root = root; }
public static void printForest(String[] links)
{
Tree t = new Tree(new TreeNode('\0'));
foreach (Tree t1 in t.buildFromLinks(links))
{
if (t1 != null)
{
t1.root.printTreeIdented("");
Console.WriteLine("");
}
}
}
// Driver Code
public static void Main(String[] args) {
String [] links1 = {"a b", "b c", "b d", "a e"};
Console.WriteLine("------------ Forest 1 ----------------");
printForest(links1);
String [] links2 = {"a b", "a g", "b c", "c d", "d e",
"c f", "z y", "y x", "x w"};
Console.WriteLine("------------ Forest 2 ----------------");
printForest(links2);
}
}
// Class to represent a tree node
public class TreeNode
{
TreeNode []children;
char c;
// Adds a child 'n' to this node
public void addChild(TreeNode n, int index)
{
this.children[index] = n;
}
// Constructor
public TreeNode(char c)
{
this.c = c; this.children = new TreeNode[26];
}
// Recursive method to print indented tree
// rooted with this node.
public void printTreeIdented(String indent)
{
Console.WriteLine(indent + "-->" + c);
foreach (TreeNode child in children)
{
if (child != null)
child.printTreeIdented(indent + " |");
}
}
}
// This code is contributed by Rajput-Ji
输出:
------------ Forest 1 ----------------
-->a
|-->b
| |-->c
| |-->d
|-->e
------------ Forest 2 ----------------
-->a
|-->b
| |-->c
| | |-->d
| | | |-->e
| | |-->f
|-->g
-->z
|-->y
| |-->x
| | |-->w
锻炼:
在上述实现中,假设输入链接的端点来自仅 26 个字符的集合。扩展端点是任意长度的字符串的实现。