二叉搜索树2

77 阅读3分钟

题目

给定一个整数 n,生成所有由 1 ... n 为节点所组成的 二叉搜索树 。

 

示例:

输入:3 输出: [   [1,null,3,2],   [3,2,null,1],   [3,1,null,null,2],   [2,1,3],   [1,null,2,null,3] ]

动态规划


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Main {


    public static void main(String[] args) {

        Main main = new Main();
        main.generateTrees(4);

    }
    public List<TreeNode> generateTrees(int n) {
        Map<Integer, List<TreeNode>> dpTable = new HashMap<>();
        for (int i = 0; i < n; i ++) {
            List<TreeNode> tempList = new ArrayList<>();
            if (i == 0) {
                TreeNode root = new TreeNode(1);
                tempList.add(root);
            }
            if (i == 1) {
                TreeNode root1 = new TreeNode(2);
                root1.left = new TreeNode(1);
                TreeNode root2 = new TreeNode(1);
                root2.right = new TreeNode(2);
                tempList.add(root1);
                tempList.add(root2);

            }
            if (i >= 2) {
                for (int j = 0; j <= i; j ++) {
                    // 右边二叉树可能的结构
                    List<TreeNode> rightList = new ArrayList<>();
                    if (j < i) {
                        // 当j右边有二叉树的时候
                        rightList = dpTable.get(i - j - 1);
                        // 刷新右边二叉树结构里的所有值
                        List<TreeNode> tempRightList = new ArrayList<>();
                        for (TreeNode rightRoot : rightList) {
                            tempRightList.add(cloneTreeNode(rightRoot, j + 1));
                        }
                        rightList = tempRightList;
                    } else {
                        rightList.add(null);
                    }

                    // 左边二叉树可能的结构
                    List<TreeNode> leftList = new ArrayList<>();
                    if (j > 0) {
                        // 当j左边有二叉树的结构时 左边的值不用刷新
                        leftList = dpTable.get(j - 1);
                    } else {
                        leftList.add(null);
                    }

                    // 组合左右二叉树
                    // 以右边为基准
                    for (TreeNode rightNode : rightList) {
                        for (TreeNode leftNode : leftList) {
                            TreeNode root = new TreeNode(j + 1);
                            root.right = rightNode;
                            root.left = leftNode;
                            tempList.add(root);
                        }

                    }
                }
            }
            dpTable.put(i, tempList);
        }

        return dpTable.get(n - 1);

    }

    // 对于相同结构, 但是节点value值不同的树重新赋值, 生成新的节点
    public TreeNode cloneTreeNode(TreeNode oldTreeNode, int value) {
        // 构造一个新的TreeNode, 在oldTreeNode的基础上, 有值的部分全部加value
        if (oldTreeNode == null) return null;
        TreeNode newTree = new TreeNode(value + oldTreeNode.val);
        newTree.left = cloneTreeNode(oldTreeNode.left, value);
        newTree.right = cloneTreeNode(oldTreeNode.right, value);
        return newTree;
    }



}

基本思路

  1. 延续二叉搜索数1的思路, 通过假定root节点为1...n的任何一个值的时候, 将问题分解为求左边子树的结果和右边子树的结果.

  2. 不同的是, 需要给出所有可能的方案, 而不是可能的种数, 那么就有个问题, 就是一个root分割开的左右子树, 虽然能变成子问题, 但是右子树的情况, 只和子问题的树的结构相同, 但是数字不同, 因此要想办法在同样的结构上赋予不同的数字(经过观察发现需要将右子树上的每个节点的值加上当前root的值即可, 需要遍历右子树), 左子树的数值是正确的无需修改

  3. 需要修改dpTbale的类型, 需要一个map, 其它的思路是一致的

  4. 左右子树组合的时候, 如果左边或者右边为空, 添加个null, 就可以解决选哪边遍历的基准问题(不然要讨论左为空, 右为空, 左右都不为空的情况)

优点

  1. 继续沿用了动态规划的思想, 并且能得到正确的答案, 只需要将问题进一步升级即可(关键是发现相同结构, 不同值的二叉树是如何生成的)

  2. 需要知道怎么遍历一个二叉树, 例如先序遍历等

缺点

  1. 同构不同值的二叉树在构造时 比较耗费时间, 效率不是很高

递归

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Main {


    public static void main(String[] args) {

        Main main = new Main();
        main.generateTrees(3);

    }

    public List<TreeNode> generateTrees(int n) {

        List<TreeNode> result = digui(1, n);
        return result;



    }

    public List<TreeNode> digui(int start, int end) {
        List<TreeNode> tempList = new ArrayList<>();
        if (start > end) {
            tempList.add(null);
            return tempList;
        }

        for (int i = start; i <= end; i ++) {
            // 将左边一串数字去生成多个子树
            List<TreeNode> leftList = digui(start, i - 1);
            // 将右边的一串数字生成子树
            List<TreeNode> rightList = digui(i + 1, end);

            // 组合左右子树 此时左右子树list里一定有值,虽然可能是null
            for (TreeNode leftNode : leftList) {
                for (TreeNode rightNdoe : rightList) {
                    // 选择根节点
                    TreeNode root = new TreeNode(i);
                    root.left = leftNode;
                    root.right = rightNdoe;
                    tempList.add(root);
                }
            }


        }
        return tempList;

    }
}

基本思路

  1. 思路会转变成自顶向下

  2. 将题目简化成如果给你1...n个数, 如何构建一个平衡二叉树, 即选取 n/2 作为root节点, 然后左边的一串数字即1... n/2 - 1, 去构造一个新的平衡二叉树作为root的左节点, 然后右边的一串数字, n / 2 + 1 ....n去构建去构造一个新的平衡二叉树作为root的右节点.

  3. 如果不是一颗平衡二叉树呢, 那么我们在选择当前root值的时候, 就不再是n/2了, 而是遍历1...n, 其它的思路不变.

  4. 每次递归返回的多个节点的时候, 要注意