给定一个整数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
- 情况二:3,4,5,1,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进行组合(双循环构造每种排列方式),就得到了整个二叉树的排列
-如下图,列举其中一种情况:
- 经如上分析有如下代码:
/*** 给定一个整数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算法