【问题标题】:Java Recursion and Binary TreeJava 递归和二叉树
【发布时间】:2016-07-20 08:12:05
【问题描述】:

我的班级一直致力于使用递归来创建诸如河内塔、斐波那契以及所有有趣的东西之类的东西。问题是,我并不是很了解所有内容。我了解递归的一般概念,但是在编写程序时将其付诸实践对我来说感觉很复杂。我知道它会一遍又一遍地调用该方法,通常在它到达它退出的基本情况下,但我很难编写代码来完成我想要它做的事情。

我们现在正在研究二叉树。我们应该使用我的教授提供的一些代码来拆分一棵树,然后编写一个递归方法来打印出树包含的所有路径。我们的输入将类似于(a(b()())(c()())),这将是一棵树:

 a
b c

b 和 c 将在其下方有 0 个子级。 (a) 是一个可能的节点,() 将是一个空节点,它将是该路径的结束。我们的目标是打印出所有路径,因此对于我的示例,输出将是:

a b

a c

我们得到的代码包括一个帮助方法,我们可以用它来编写我们的递归方法:

public class BinaryTree {


public static void main(String[] args) {
    Scanner scan = new Scanner(System.in);
    String tree = scan.nextLine();//correct format : (a()())
    String[] t = splitTree(tree);
    System.out.println(Arrays.toString(t));

}
public static String[] splitTree(String tree)
{
    //expected format
    //(node tree tree)
    //0 1 2-x x-(length-2) length-1
    if(tree.length() <= 2)//tree not long enough to process
        return new String[]{tree};

    String[] temp = new String[3];
    temp[0] = "" + tree.charAt(1);//grab tree node
    tree = tree.substring(2, tree.length()-1);//remove node and outer paren
    int parenCount = 0;//count of open paren
    int endTreeOne = 0;//end of first tree
    for(int i = 0; i < tree.length(); i++)
    {
        if(tree.charAt(i) == '(')
            parenCount++;
        if(tree.charAt(i) == ')')
            parenCount--;
        if(parenCount == 0)
        {
            endTreeOne = i;
            break;//ends for loop early
        }
    }
    temp[1] = tree.substring(0, endTreeOne+1);//left tree
    temp[2] = tree.substring(endTreeOne+1);//right tree
    return temp;
}

这个方法基本上是把(a(b()())(c()()))这样的字符串转换成[a, (b()()), (c()())]。基本上将树拆分。

我真的不确定如何从这里开始编写我的递归方法。老实说,我感到非常迷茫(因此感到沮丧)。我想我需要让我的方法检查“()”是否存在,那就是路径的尽头。这会是我退出我需要的循环的基本情况吗?我不确定如何指定树的哪一侧。如果有人可以提供任何帮助、提示或让我找到解决此问题的正确思路,我将不胜感激。

【问题讨论】:

  • 通常树会与面向对象编程一起教授...如果树有 BinaryTree leftBinaryTree right 变量,这将非常容易,因为您只需打印根,然后递归打印左树,然后递归打印右树。
  • @cricket_007,正确的想法,但 OP 并没有要求树的打印表示。相反,对于每个叶子,打印从根到叶子的路径。该程序可以按照您描述的查找叶子的相同方式遍历树,但是当它打印时,以及沿途要记住的内容有些不同。
  • 对,我应该打印从根到每个分支末尾的路径。我只是对如何开始这件事感到有点迷茫。 :(
  • 我有可以解析字符串的代码。这对您来说足够了吗,因为您的问题主要是两个部分?
  • 它是如何解析字符串的?我可以看看吗?

标签: java recursion tree binary-tree


【解决方案1】:

我觉得使用树的对象“打印路径”步骤会更容易,所以让我们定义一下。

toString 方法被递归实现以重新打印您将解析到该对象中的内容。

public class BinaryTreeNode {
    public String root;
    public BinaryTreeNode left;
    public BinaryTreeNode right;

    public BinaryTreeNode(String root) {
        this.root = root;
    }

    @Override
    public String toString() {
        String s = root;

        if (left != null) {
            s += left.toString();
        } else {
            s += "()";
        }

        if (right != null) {
            s += right.toString();
        } else {
            s += "()";
        }

        return "(" + s + ")";
    }
}

我还有一个辅助方法来计算括号,如果不匹配则抛出错误。

private static int getParenCount(String s) {
    int parenCount = 0;
    int opened = 0;

    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (c == '(') {
            parenCount++;
            opened++;
        } else if (c == ')') {
            parenCount++;
            opened--;
        }
    }
    if (opened != 0) {
        throw new IllegalArgumentException("Paren mismatch." + s + " is not a valid tree.");
    }
    return parenCount;
}

然后,关于解析字符串,我留下了一些有用的代码cmets。

public static BinaryTreeNode getTree(String treeString) {
    // Initialize variables
    String root;
    String leftTree;
    String rightTree;
    BinaryTreeNode tree = new BinaryTreeNode("");

    System.out.println("Input: " + treeString);

    if (treeString.equals("()")) {
        System.out.println("Empty tree. Returning!");
        return null;
    }

    // Check for even parenthesis
    int parenCount = getParenCount(treeString);
    if (parenCount % 2 == 0) {

        // Strip the outside parenthesis
        treeString = treeString.substring(1, treeString.length()-1);
        System.out.println("tree: " + treeString);

        // Find the first '(' because the root is everything before it
        int leftTreeStart = treeString.indexOf('(');
        root = treeString.substring(0, leftTreeStart);

        // Find the complete left-tree
        int leftTreeEnd = leftTreeStart + 1;
        int leftTreeParenCount = 0;
        for (int i = leftTreeStart-1; i < treeString.length(); i++) {
            char c = treeString.charAt(i);
            if (c == '(') {
                leftTreeParenCount++;
            } else if (c == ')') {
                leftTreeParenCount--;
                if (leftTreeParenCount == 0) {
                    leftTreeEnd = i;
                    break;
                }
            }
        }

        System.out.println("root: " + root);
        tree.root = root;

        leftTree = treeString.substring(leftTreeStart, leftTreeEnd + 1);
        System.out.println("\nleft: " + leftTree);
        System.out.println("Recurse left...");
        tree.left = getTree(leftTree); // recurse here

        // The right-tree is just the remainder of the string
        rightTree = treeString.substring(leftTreeEnd + 1);
        System.out.println("\nright:" + rightTree);
        System.out.println("Recurse right...");
        tree.right = getTree(rightTree); // recurse here

    }

    return tree; 
}

【讨论】:

  • 非常感谢这篇文章。我将通过它并拼凑你是如何做到这一点的,并尝试将一些想法/代码整合到我自己的工作中。 :)(另外,感谢 cmets - 绝对可以帮助我了解您在做什么)
  • 欢迎。如果您一开始不了解递归,请不要担心。实际上,我不得不重新参加向我介绍它的课程。现在我只考虑“什么是最小输入?”,然后将其用作基本情况,然后想“我怎样才能得到一个将较小输入的结果用于较大输入的值?”
  • 知道递归有时对人们来说是一个绊脚石,这让我感觉好多了。我的意思是,我理解它在做什么的概念,但只是将其付诸实践有点令人困惑。只是需要更多的经验,我猜。 :)
猜你喜欢
  • 2010-12-07
  • 1970-01-01
  • 1970-01-01
  • 2013-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-19
  • 2014-03-29
相关资源
最近更新 更多