数据结构与算法--求1~n能组成的所有二叉搜索树的排列

给定一个整数n,生成并返回所有N个节点组成并且节点值从1到n互不相同的不同二叉树,可以按照任意顺序

  • 二叉树文章列表:

数据结构与算法–面试必问AVL树原理及实现

数据结构与算法–二叉树的深度问题

数据结构与算法–二叉堆(最大堆,最小堆)实现及原理

数据结构与算法–二叉查找树转顺序排列双向链表

数据结构与算法-- 二叉树中和为某一值的路径

数据结构与算法-- 二叉树后续遍历序列校验

数据结构与算法-- 广度优先打印二叉树

数据结构与算法–解决问题的方法- 二叉树的的镜像

数据结构与算法–重建二叉树

数据结构与算法–二叉查找树实现原理

数据结构与算法–二叉树实现原理

数据结构与算法–B树原理及实现

数据结构与算法–数字在排序数组中出现次数

数据结构与算法–死磕二叉树

数据结构与算法–二叉树第k个大的节点

数据结构与算法–求1~n能组成的所有二叉搜索树的排列

全排列问题

  • 排列组合的问题在之前的文中我们已经求解过:
    数据结构与算法–字符串的排列组合问题
    数据结构与算法–代码完整性案例分析

  • 在求数组全排列的过程中,我们将数组的每一位看成是独立的,接着每一位上分别排列1~9 的每一位树,一次对数组的n为进行递归,得到我们的全排列。

  • 现题中需要让1~n 组成n 个阶段的不同二叉树,也就是我们需要求解二叉树的全排列

方法一:转为数组全排列实现

  • 在构造二叉树时候,我们将比root 节点小的放左边,大的放右边,那么如果有 从1~n的数组依次对每一个节点构造二叉树的方式进行insert。那么我们能够得到一颗二叉树 T1

  • 如果我们修改数组的顺序,修改成 k…k-1, 1, k+1,…n-1, n,接着生成二叉树T2

  • 那么T1和 T2 大概率是不同的,特殊情况如下:

  • 情况一:3,1,2,4,5

数据结构与算法--求1~n能组成的所有二叉搜索树的排列_第1张图片

  • 情况二:3,4,5,1,2

数据结构与算法--求1~n能组成的所有二叉搜索树的排列_第2张图片

  • 如上两种情况,在中间节点是root节点时,大节点列表4,5 与小节点列表 1,2 分别在两侧,不管数组如何排列,生成的二叉树是相同的。

  • 经过如上分析,那么我们有如下的实现方案:

    • 生成一个1~n的数组,并且求数组的全排列
    • 每生成一个不同的数组排列,将该数组构建成一个二叉排序树,并且记录改二叉排序树的前序遍历生成的字符。并存储在数组 StrLIst中
    • 如果 StrList中存在 元素 k 与当前生成的二叉树的前序遍历字符一致,那么说明这种情况以及生成过
    • 如果StrList中不存在,那么我们将新构建的NewBinary添加到我们的二叉排序树数组 BinaryList中。
  • 如上分析有如下代码:

/**
 * 给定一个整数n,生成并返回所有N个节点组成并且节点值从1到n互不相同的不同二叉树,可以按照任意顺序
 * 解析:求解1~n能组成多少个不同的二叉搜索树
 * @author liaojiamin
 * @Date:Created in 10:15 2021/7/22
 */
public class BuildGenerateTrees {

    public static void main(String[] args) {
        List<BinaryNode> binaryNodes = new ArrayList<>();
        List<String> middleSearch  = new ArrayList<>();
        binaryNodes = generateTrees(4);
        for (int i = 0; i < binaryNodes.size(); i++) {
            StringBuilder strBu = new StringBuilder();
            middleSearch.add(printTree(binaryNodes.get(i),strBu));
        }
        middleSearch.forEach(System.out::println);
    }

    /**
     * 方法一
     * 构造二叉树全排列:构造数组全排列,数组构造成二叉树,通过前序遍历值筛选二叉树
     * */
    public static List<BinaryNode> generateTrees(int n){
        List<BinaryNode> binaryNodes = new ArrayList<>();
        List<String> middleSearchStr = new ArrayList<>();
        int[] array = new int[n];
        for (int i = 1; i <= n; i++) {
            array[i-1] = i;
        }
        return buildGenerateTree(array, binaryNodes, 0, middleSearchStr);
    }

    /**
     * 生成数组的全排列,每种排列按顺序生成二叉树
     * */
    public static List<BinaryNode> buildGenerateTree(int[] array, List<BinaryNode> binaryNodes, int start, List<String> middleSearchStr){
        if(array == null || array.length <=1 || start == (array.length -1)){
            BinaryNode newNode = buildBinarySearchTree(array);
            StringBuilder str = new StringBuilder();
            String middleStr = printTree(newNode, str );
            if(!middleSearchStr.contains(middleStr)){
                middleSearchStr.add(middleStr);
                binaryNodes.add(newNode);
            }
        }
        for (int i = start; i < array.length; i++) {
            int temp = array[i];
            array[i] = array[start];
            array[start] = temp;
            buildGenerateTree(array, binaryNodes, start+1, middleSearchStr);
            temp = array[i];
            array[i] = array[start];
            array[start] = temp;
        }
        return binaryNodes;
    }

    /**
     * 前序遍历字符集合
     * */
    public static String printTree(BinaryNode t, StringBuilder strbu) {
        if (t == null || t.getElement() == null) {
            return strbu.toString();
        }
        for (int i = 0; i < t.getCount(); i++) {
            strbu.append(t.getElement() + ":" + t.getHeight()+" ");
        }
        printTree(t.getLeft(), strbu);
        printTree(t.getRight(), strbu);
        return strbu.toString();
    }

    /**
     * 根据数组构造二叉树
     * */
    public static BinaryNode buildBinarySearchTree(int[] array){
        if(array == null || array.length <= 0){
            return null;
        }
        BinaryNode node = new BinaryNode(null, null, null);
        for (int i = 0; i < array.length; i++) {
            node = insertNode(node, array[i]);
        }
        return node;
    }

    /**
     * 插入节点构造二叉搜索树
     * */
    public static BinaryNode insertNode(BinaryNode node, Integer k){
        if(k == null){
            return node;
        }
        if(node == null || node.getElement() == null){
            node = new BinaryNode(k, null, null);
        }
        int validateK = node.compareTo(k);
        if(validateK > 0){
            node.setLeft(insertNode(node.getLeft(), k));
        }else if (validateK < 0){
            node.setRight(insertNode(node.getRight(), k));
        }
        return node;
    }
}
  • 如上实现方案时间复杂度在O(n2), 空间复杂度存在两部分,一部分二叉搜索树数组,这部分取决于节点数量,数量不同排列的个数不同,另一部分在于存储二叉排序树的前序遍历字符串,因此空间复杂度也不会小。

方法二:分治法

  • 方案一的时间复杂度,空间复杂度都不是太理想,第一想法实现方案往往题目没有这么简单,在方法一中我们实现我们是受到之前文章中思路的影响,将之前的排列组合的思路套用在 二叉搜索树的排列上,但是因为两种数据结构本身复杂度就相差很大,导致二叉搜索树的排列问题异常复杂。
  • 我们换一种思路,这个也是看到别的思路,看到后就茅塞顿开,因为一旦思路被固定住,要想跳出来还是比较困难的,就和写小说的作者肯定不会去看别人写的小说是一个道理。
  • 我们直接将数组全排列思想用到二叉树搜索树中:
    • 在1~n中每个数字都有可能是根节点,确定根节点后 k,k 之前的数据 1 ~ k就是左子树,在k ~ n中每一个就是右子树
    • 那么我们遍历1 ~ n中每一位,让每个数都构造成不同根的 一棵二叉搜索树
    • 接着处理左子树 1 ~ k,同样,1~k中的左子树也是一棵二叉搜索树,依然套用以上逻辑,右子树同理。分别记录为leftList, rightLIst
    • 那么构造完左右子树后,我们只需要在左子树 leftList,rightList进行组合(双循环构造每种排列方式),就得到了整个二叉树的排列
      -如下图,列举其中一种情况:

数据结构与算法--求1~n能组成的所有二叉搜索树的排列_第3张图片

数据结构与算法--求1~n能组成的所有二叉搜索树的排列_第4张图片
数据结构与算法--求1~n能组成的所有二叉搜索树的排列_第5张图片

数据结构与算法--求1~n能组成的所有二叉搜索树的排列_第6张图片
数据结构与算法--求1~n能组成的所有二叉搜索树的排列_第7张图片

  • 经如上分析有如下代码:
/**
 * 给定一个整数n,生成并返回所有N个节点组成并且节点值从1到n互不相同的不同二叉树,可以按照任意顺序
 * 解析:求解1~n能组成多少个不同的二叉搜索树
 * @author liaojiamin
 * @Date:Created in 10:15 2021/7/22
 */
public class BuildGenerateTrees {

    public static void main(String[] args) {
        List<BinaryNode> binaryNodes = generateTreesBinary(1, 4);
        BinarySearchTree binarySearchTree = new BinarySearchTree();
        for (BinaryNode binaryNode : binaryNodes) {
            binarySearchTree.printTree(binaryNode);
        }
    }


    /**
     * 方法二
     * 分治法
     * */
    public static List<BinaryNode>generateTreesBinary(int start, int end){
        List<BinaryNode> binaryNodes = new LinkedList<>();
        if(start > end){
            binaryNodes.add(null);
            return binaryNodes;
        }
        for (int i=start;i<=end;i++){
            List<BinaryNode> leftNode = generateTreesBinary(start, i-1);
            List<BinaryNode> rightNode = generateTreesBinary(i+1, end);
            for (BinaryNode left : leftNode) {
                for (BinaryNode right : rightNode) {
                    binaryNodes.add(new BinaryNode(i, left, right));
                }
            }
        }
        return binaryNodes;
    }
}

  • 以上思路理解起来更清晰,代码实现方式也更简单,时间复杂度还是取决于n的大小,假设n个阶段能构建Cn棵二叉搜索树,那么时间复杂度一棵树生成需要O(n),Cn棵树O(n*Cn)
  • 空间复杂度,每一棵树都有n个阶段,有Cn棵树,总空间复杂度O(n*Cn)

上一篇:数据结构与算法–二叉树第k个大的节点
下一篇:数据结构与算法一篇帮助你吃下KMP算法

你可能感兴趣的:(算法,数据结构,数据结构,算法)