从字符串数组递归创建二叉解析树
Recursively creating a binary parse tree from an array of strings
我正在尝试为前缀表达式创建解析树。
创建的节点有一个父指针和一个布尔标识运算符。
我的问题是,一旦我到达 6(树左侧的末端)
根指针保持为15。
所以数组继续变小,但递归永远不会返回到原始根,以便填充树的右侧。
public class demo {
public static void main(String[] args){
String[] in = {"3","*","4","*","15", "6","+","/","14","3","*","65","5"};
TreeNode root = new TreeNode("+");
TreeNode nuw = new TreeNode("-");
System.out.println(root.data);
populate(in,root,nuw);
}
public static TreeNode populate(String[] array, TreeNode root, TreeNode nuw){
TreeNode current = root;
String[] copy = new String[array.length-1];
System.arraycopy(array, 1, copy, 0, array.length-1);
TreeNode next = new TreeNode(copy[0]);
if(current==null){
return null;
}
if(current.left==null && !nuw.operator){
current.left=nuw;
nuw.parent = current;
}else if(current.left!=null && current.right==null && !nuw.operator){
current.right = nuw;
nuw.parent = current;
}else if(nuw.operator && current.left==null){
current.left=next;
next.parent=current;
current=next;
}else if(nuw.operator && current.left!=null && current.right==null){
current.right=next;
next.parent=current;
current=next;
}else if(current.right!=null && current.left!=null){
current = current.parent;
}
for(int i=0;i<array.length;i++)System.out.print(array[i]);
System.out.println("\n current: "+current.data);
System.out.println("next: "+next.data);
return populate(copy,current,next);
}
}
树节点class
public class TreeNode {
protected String data;
protected boolean operator;
protected TreeNode left,right,parent;
public TreeNode(String x){
this.left = null;
this.right = null;
this.parent = null;
if(x.equals("+")||x.equals("-")||x.equals("*")||x.equals("/")||x.equals("%")){
this.operator=true;
this.data=x;
}else{
try{
Integer.parseInt(x);
this.data=x;
this.operator=false;
}catch(NumberFormatException e){
System.out.println("Invalid Input");
}
}
}
}
好的,我放弃了你的解决方案并为它实现了我的版本。
您的图表中有一些循环,因为有时如果您的 TreeNode
会引用自身或其父项作为其 left
。我的方法 getMaxWidthOfChildren()
将展示这一点并与您的代码兼容。所以你可以在你的代码中使用它来查看问题。
但是还涉及另一个问题:一旦您必须向上迭代不止一个步骤,您的 'array indexing'(或者在您的情况下:在 EACH 调用中从数组中删除第一个数组元素)就会失步.您删除了太多元素,因此丢失了剩余单元格的元素和轨迹。
现在我的解决方案:
- 这更直接,因为它是自然递归的,完全符合你的(depth-first)first-order逻辑的意图,每个构造函数自己决定并获取所需的数据。
- 这里的大技巧是 - 类似于在数组上推进索引 - 使用
Stack
,这里是 LinkedList
的形式。 (java.util.Stack 是 thread-safe,这里不需要,所以我使用它的现代对应物 LinkedList
)
- 另外,您可以将整个数据串重新输入,无需任何人为的附加注释和变量。
所以构造真的很容易,但是要正确实现 toString()
方法有点困难。
package Whosebug;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
public class PopulateTree {
static public class TreeNode {
protected String data;
protected boolean isOperator;
protected TreeNode left, right, parent;
public TreeNode(final LinkedList<String> pQueue, final TreeNode pParent) {
data = pQueue.poll();
isOperator = isOperator(this.data);
parent = pParent;
left = isOperator ? new TreeNode(pQueue, this) : null;
right = isOperator ? new TreeNode(pQueue, this) : null;
}
// it's likely we need this functionality method somewhere else, too, so we put it in its own method
// also, it makes the CTOR so much shorter and its use obvious
static public boolean isOperator(final String pString) {
return pString.equals("+") || pString.equals("-") || pString.equals("*") || pString.equals("/") || pString.equals("%");
}
public int getMaxWidthOfChildren() {
return getMaxWidthOfChildren(new HashSet<>());
}
private int getMaxWidthOfChildren(final HashSet<TreeNode> pAlreadyVisitedNodes) {
if (pAlreadyVisitedNodes.contains(this)) {
System.out.println("Error: recursive loop for " + this.data);
return 0;
} else {
pAlreadyVisitedNodes.add(this);
}
int ret = 1;
if (left != null) ret += left.getMaxWidthOfChildren(pAlreadyVisitedNodes);
if (right != null) ret += right.getMaxWidthOfChildren(pAlreadyVisitedNodes);
return ret;
}
public int getMaxDepth() {
final int l = left == null ? 0 : left.getMaxDepth();
final int r = right == null ? 0 : right.getMaxDepth();
return 1 + Math.max(l, r);
}
@Override public String toString() {
final int maxDepth = getMaxDepth();
System.out.println("maxDepth: " + maxDepth);
final int maxWidth = (int) (Math.pow(2, maxDepth - 2) * 2 - 1);
System.out.println("maxWidth: " + maxWidth);
final String[][] out = new String[maxDepth][maxWidth];
// fill array with data
final int center = maxWidth / 2;
fillArray(out, maxWidth, center, this, 0);
// form array into String
final StringBuilder sb = new StringBuilder();
for (int y = 0; y < out.length; y++) {
for (int x = 0; x < out[y].length; x++) {
final String dat = out[y][x];
final String text = dat == null ? "" : dat;
sb.append(text + "\t");
}
sb.append("\n");
}
return sb.toString();
}
private void fillArray(final String[][] pOut, final int pWidth, final int pCenter, final TreeNode pTreeNode, final int pLineIndex) {
if (pTreeNode == null) return;
System.out.println("Filling: w=" + pWidth + "\tc=" + pCenter + "\tl=" + pLineIndex);
pOut[pLineIndex][pCenter] = pTreeNode.data;
final int newWidth = pWidth / 2;
final int leftCenter = pCenter - newWidth / 2 - 1;
final int rightCenter = pCenter + newWidth / 2 + 1;
fillArray(pOut, newWidth, leftCenter, pTreeNode.left, pLineIndex + 1);
fillArray(pOut, newWidth, rightCenter, pTreeNode.right, pLineIndex + 1);
}
}
public static void main(final String[] args) {
final String[] in = { "-", "+", "3", "*", "4", "*", "15", "6", "+", "/", "14", "3", "*", "65", "5" };
final LinkedList<String> queue = new LinkedList<>(Arrays.asList(in));
final TreeNode root = new TreeNode(queue, null);
System.out.println();
System.out.println("Printing Root Node:");
System.out.println(root);
}
}
我正在尝试为前缀表达式创建解析树。 创建的节点有一个父指针和一个布尔标识运算符。
我的问题是,一旦我到达 6(树左侧的末端) 根指针保持为15。 所以数组继续变小,但递归永远不会返回到原始根,以便填充树的右侧。
public class demo {
public static void main(String[] args){
String[] in = {"3","*","4","*","15", "6","+","/","14","3","*","65","5"};
TreeNode root = new TreeNode("+");
TreeNode nuw = new TreeNode("-");
System.out.println(root.data);
populate(in,root,nuw);
}
public static TreeNode populate(String[] array, TreeNode root, TreeNode nuw){
TreeNode current = root;
String[] copy = new String[array.length-1];
System.arraycopy(array, 1, copy, 0, array.length-1);
TreeNode next = new TreeNode(copy[0]);
if(current==null){
return null;
}
if(current.left==null && !nuw.operator){
current.left=nuw;
nuw.parent = current;
}else if(current.left!=null && current.right==null && !nuw.operator){
current.right = nuw;
nuw.parent = current;
}else if(nuw.operator && current.left==null){
current.left=next;
next.parent=current;
current=next;
}else if(nuw.operator && current.left!=null && current.right==null){
current.right=next;
next.parent=current;
current=next;
}else if(current.right!=null && current.left!=null){
current = current.parent;
}
for(int i=0;i<array.length;i++)System.out.print(array[i]);
System.out.println("\n current: "+current.data);
System.out.println("next: "+next.data);
return populate(copy,current,next);
}
}
树节点class
public class TreeNode {
protected String data;
protected boolean operator;
protected TreeNode left,right,parent;
public TreeNode(String x){
this.left = null;
this.right = null;
this.parent = null;
if(x.equals("+")||x.equals("-")||x.equals("*")||x.equals("/")||x.equals("%")){
this.operator=true;
this.data=x;
}else{
try{
Integer.parseInt(x);
this.data=x;
this.operator=false;
}catch(NumberFormatException e){
System.out.println("Invalid Input");
}
}
}
}
好的,我放弃了你的解决方案并为它实现了我的版本。
您的图表中有一些循环,因为有时如果您的 TreeNode
会引用自身或其父项作为其 left
。我的方法 getMaxWidthOfChildren()
将展示这一点并与您的代码兼容。所以你可以在你的代码中使用它来查看问题。
但是还涉及另一个问题:一旦您必须向上迭代不止一个步骤,您的 'array indexing'(或者在您的情况下:在 EACH 调用中从数组中删除第一个数组元素)就会失步.您删除了太多元素,因此丢失了剩余单元格的元素和轨迹。
现在我的解决方案:
- 这更直接,因为它是自然递归的,完全符合你的(depth-first)first-order逻辑的意图,每个构造函数自己决定并获取所需的数据。
- 这里的大技巧是 - 类似于在数组上推进索引 - 使用
Stack
,这里是LinkedList
的形式。 (java.util.Stack 是 thread-safe,这里不需要,所以我使用它的现代对应物LinkedList
) - 另外,您可以将整个数据串重新输入,无需任何人为的附加注释和变量。
所以构造真的很容易,但是要正确实现 toString()
方法有点困难。
package Whosebug;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
public class PopulateTree {
static public class TreeNode {
protected String data;
protected boolean isOperator;
protected TreeNode left, right, parent;
public TreeNode(final LinkedList<String> pQueue, final TreeNode pParent) {
data = pQueue.poll();
isOperator = isOperator(this.data);
parent = pParent;
left = isOperator ? new TreeNode(pQueue, this) : null;
right = isOperator ? new TreeNode(pQueue, this) : null;
}
// it's likely we need this functionality method somewhere else, too, so we put it in its own method
// also, it makes the CTOR so much shorter and its use obvious
static public boolean isOperator(final String pString) {
return pString.equals("+") || pString.equals("-") || pString.equals("*") || pString.equals("/") || pString.equals("%");
}
public int getMaxWidthOfChildren() {
return getMaxWidthOfChildren(new HashSet<>());
}
private int getMaxWidthOfChildren(final HashSet<TreeNode> pAlreadyVisitedNodes) {
if (pAlreadyVisitedNodes.contains(this)) {
System.out.println("Error: recursive loop for " + this.data);
return 0;
} else {
pAlreadyVisitedNodes.add(this);
}
int ret = 1;
if (left != null) ret += left.getMaxWidthOfChildren(pAlreadyVisitedNodes);
if (right != null) ret += right.getMaxWidthOfChildren(pAlreadyVisitedNodes);
return ret;
}
public int getMaxDepth() {
final int l = left == null ? 0 : left.getMaxDepth();
final int r = right == null ? 0 : right.getMaxDepth();
return 1 + Math.max(l, r);
}
@Override public String toString() {
final int maxDepth = getMaxDepth();
System.out.println("maxDepth: " + maxDepth);
final int maxWidth = (int) (Math.pow(2, maxDepth - 2) * 2 - 1);
System.out.println("maxWidth: " + maxWidth);
final String[][] out = new String[maxDepth][maxWidth];
// fill array with data
final int center = maxWidth / 2;
fillArray(out, maxWidth, center, this, 0);
// form array into String
final StringBuilder sb = new StringBuilder();
for (int y = 0; y < out.length; y++) {
for (int x = 0; x < out[y].length; x++) {
final String dat = out[y][x];
final String text = dat == null ? "" : dat;
sb.append(text + "\t");
}
sb.append("\n");
}
return sb.toString();
}
private void fillArray(final String[][] pOut, final int pWidth, final int pCenter, final TreeNode pTreeNode, final int pLineIndex) {
if (pTreeNode == null) return;
System.out.println("Filling: w=" + pWidth + "\tc=" + pCenter + "\tl=" + pLineIndex);
pOut[pLineIndex][pCenter] = pTreeNode.data;
final int newWidth = pWidth / 2;
final int leftCenter = pCenter - newWidth / 2 - 1;
final int rightCenter = pCenter + newWidth / 2 + 1;
fillArray(pOut, newWidth, leftCenter, pTreeNode.left, pLineIndex + 1);
fillArray(pOut, newWidth, rightCenter, pTreeNode.right, pLineIndex + 1);
}
}
public static void main(final String[] args) {
final String[] in = { "-", "+", "3", "*", "4", "*", "15", "6", "+", "/", "14", "3", "*", "65", "5" };
final LinkedList<String> queue = new LinkedList<>(Arrays.asList(in));
final TreeNode root = new TreeNode(queue, null);
System.out.println();
System.out.println("Printing Root Node:");
System.out.println(root);
}
}