根据数组构造二叉树

从中序与后序遍历序列构造二叉树

题目描述

力扣链接🔗

根据数组构造二叉树 - 图1

题目分析

大体思路是根据后序遍历的最后一个节点可以知道根节点,然后再通过根节点先切割中序数组,中序遍历中切割数组,此时根节点的左侧就是左孩子的中序数组,根节点的右侧就是右孩子的中序数组。(先切割中序数组的原因是后序数组的切割需要中序数组来作为判断)然后再根据中序数组切割后序数组,再进行递归。

根据数组构造二叉树 - 图2

代码分析

步骤:

  • 第一步:如果数组大小为零的话,说明是空节点了。
  • 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
  • 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
  • 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
  • 第五步:切割后序数组,切成后序左数组和后序右数组
  • 第六步:递归处理左区间和右区间

注意:再切割的过程中统一使用的是左闭右开的区间进行分割。

后序数组的切割的话需要根据中序数组左右子树数组的长度进行切割。就是中序数组大小一定是和后序数组的大小相同的(这是必然)。

中序数组我们都切成了左中序数组和右中序数组了,那么后序数组就可以按照左中序数组的大小来切割,切成左后序数组和右后序数组。

  1. class Solution {
  2. public TreeNode buildTree(int[] inorder, int[] postorder) {
  3. return traversal(inorder, 0, inorder.length, postorder, 0, postorder.length);
  4. }
  5. /**
  6. * @param inorder 中序数组
  7. * @param inLeft 中序左孩子数组
  8. * @param inRight 中序右孩子数组
  9. * @param postorder 后序数组
  10. * @param postLeft 后序左孩子数组
  11. * @param postRight 后序右孩子数组
  12. * @return
  13. */
  14. public TreeNode traversal(
  15. int[] inorder, int inLeft, int inRight,
  16. int[] postorder, int postLeft, int postRight
  17. ) {
  18. // 第一步:如果数组大小为零的话,说明是空节点了。
  19. if (inRight - inLeft < 1) {
  20. return null;
  21. }
  22. // 只有一个元素也直接返回
  23. if (inRight - inLeft == 1) {
  24. return new TreeNode(inorder[inLeft]);
  25. }
  26. // 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
  27. int rootValue = postorder[postRight - 1];
  28. // 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
  29. int rootIndex = 0;
  30. for (int i = inLeft; i < inRight; i++) { // 注意停止条件不能为 inorder.length - 1,会变化
  31. if (inorder[i] == rootValue) {
  32. rootIndex = i;
  33. break;
  34. }
  35. }
  36. // 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
  37. // 第五步:切割后序数组,切成后序左数组和后序右数组
  38. // 第六步:递归处理左区间和右区间
  39. TreeNode root = new TreeNode(rootValue); // 生成节点
  40. // 统一采用左闭右开切割
  41. root.left = traversal(inorder, inLeft, rootIndex,
  42. postorder, postLeft, postLeft + (rootIndex - inLeft));
  43. root.right = traversal(inorder, rootIndex + 1, inRight,
  44. postorder, postLeft + (rootIndex - inLeft), postRight - 1);
  45. return root;
  46. }
  47. }

从前序与中序遍历序列构造二叉树

题目描述

力扣链接🔗

根据数组构造二叉树 - 图3

代码分析

思路和上题一致,注意区间分割还是按照左闭右开的区间进行分割。

  1. class ConstructBinaryTreeFromPreorderAndInorderTraversal {
  2. public static void main(String[] args) {
  3. Solution solution = new ConstructBinaryTreeFromPreorderAndInorderTraversal().new Solution();
  4. }
  5. //leetcode submit region begin(Prohibit modification and deletion)
  6. public class TreeNode {
  7. int val;
  8. TreeNode left;
  9. TreeNode right;
  10. TreeNode() {
  11. }
  12. TreeNode(int val) {
  13. this.val = val;
  14. }
  15. TreeNode(int val, TreeNode left, TreeNode right) {
  16. this.val = val;
  17. this.left = left;
  18. this.right = right;
  19. }
  20. }
  21. class Solution {
  22. public TreeNode buildTree(int[] preorder, int[] inorder) {
  23. return traversal(inorder, 0, inorder.length, preorder, 0, preorder.length);
  24. }
  25. /**
  26. * @param inorder 中序数组
  27. * @param inLeft 中序左孩子数组
  28. * @param inRight 中序右孩子数字
  29. * @param preorder 前序数组
  30. * @param preLeft 前序左孩子数组
  31. * @param preRight 前序右孩子数组
  32. * @return
  33. */
  34. public TreeNode traversal(
  35. int[] inorder, int inLeft, int inRight,
  36. int[] preorder, int preLeft, int preRight
  37. ) {
  38. if (preRight - preLeft < 1) {
  39. return null;
  40. }
  41. if (preRight - preLeft == 1) {
  42. return new Tre eNode(preorder[preLeft]);
  43. }
  44. // 找出根节点
  45. int rootValue = preorder[preLeft];
  46. int rootIndex = 0;
  47. for (int i = inLeft; i <= inRight; i++) {
  48. if (inorder[i] == rootValue) {
  49. rootIndex = i;
  50. break;
  51. }
  52. }
  53. TreeNode root = new TreeNode(rootValue);
  54. // 统一按照左闭右开切割
  55. root.left = traversal(
  56. inorder, inLeft, rootIndex,
  57. preorder, preLeft + 1, preLeft + (rootIndex - inLeft) + 1
  58. );
  59. root.right = traversal(
  60. inorder, rootIndex + 1, inRight,
  61. preorder, preLeft + (rootIndex - inLeft) + 1, preRight
  62. );
  63. return root;
  64. }
  65. }

最大二叉树

题目描述

力扣链接🔗

根据数组构造二叉树 - 图4

题目思路

大体思路也是分割路径,先寻找最大的元素索引,在切割数组依次递归,大体模板和上面2题一致。

根据数组构造二叉树 - 图5

  • 确定递归函数的参数和返回值
    参数就是传入的是存放元素的数组,返回该数组构造的二叉树的头结点,返回类型是指向节点的指针。
  • 确定终止条件
    题目中说了输入的数组大小一定是大于等于1的,所以我们不用考虑小于1的情况,那么当递归遍历的时候,如果传入的数组大小为1,说明遍历到了叶子节点了。
    那么应该定义一个新的节点,并把这个数组的数值赋给新的节点,然后返回这个节点。 这表示一个数组大小是1的时候,构造了一个新的节点,并返回。
  • 确定单层递归的逻辑
    单层逻辑:进行左右孩子数组的切割,使用左闭右开的原则进行切割。

代码

  1. class Solution {
  2. public TreeNode constructMaximumBinaryTree(int[] nums) {
  3. return traversal(nums, 0, nums.length);
  4. }
  5. public TreeNode traversal(int[] nums, int left, int right) {
  6. if (right - left < 1) {
  7. return null;
  8. }
  9. if (right - left == 1) {
  10. return new TreeNode(nums[left]);
  11. }
  12. // 获取最大值的索引
  13. int maxIndex = left;
  14. for (int i = left + 1; i < right; i++) { // 一定注意起始和结束范围不是0到数组最后一位
  15. if (nums[i] > nums[maxIndex]) {
  16. maxIndex = i; // 不能加break,因为需要找到最大的值索引
  17. }
  18. }
  19. // 左闭右开的分割
  20. TreeNode root = new TreeNode(nums[maxIndex]);
  21. root.left = traversal(nums, left, maxIndex);
  22. root.right = traversal(nums, maxIndex + 1, right);
  23. return root;
  24. }
  25. }

构建二叉搜索树

题目描述

🔗
image.png

解题思路

可知数组已按升序排列,那么构建二叉搜索树应该从中间的数字开始狗仔为一棵平衡树(也可以不构造平衡树,但何必为难自己)

递归构造方法模板修改即可

  1. public TreeNode sortedArrayToBST(int[] nums) {
  2. if (nums.length == 0) {
  3. return null;
  4. }
  5. return traversal(nums, 0, nums.length);
  6. }
  7. public TreeNode traversal(int[] nums, int left, int right) {
  8. if (right - left == 1) {
  9. return null;
  10. }
  11. // 找到中间的节点
  12. int mid = (left + right) / 2;
  13. TreeNode root = new TreeNode(nums[mid]);
  14. // 递归构造
  15. root.left = traversal(nums, left, mid);
  16. root.right = traversal(nums, mid, right);
  17. return root;
  18. }

迭代法

用三个队列分别存储节点和左右边界进行构造。

  1. /**
  2. * 迭代法
  3. *
  4. * @param nums
  5. * @return
  6. */
  7. public TreeNode sortedArrayToBST(int[] nums) {
  8. if (nums.length == 0) {
  9. return null;
  10. }
  11. Queue<Integer> leftQueue = new LinkedList<>(); // 左边界队列
  12. Queue<Integer> rightQueue = new LinkedList<>(); // 右边界队列
  13. Queue<TreeNode> nodeQueue = new LinkedList<>(); // 节点队列
  14. // 初始化队列,整体采用左闭右开划分数组
  15. TreeNode root = new TreeNode(0);
  16. leftQueue.offer(0);
  17. rightQueue.offer(nums.length - 1);
  18. nodeQueue.offer(root);
  19. while (!nodeQueue.isEmpty()) {
  20. TreeNode node = nodeQueue.poll();
  21. Integer left = leftQueue.poll();
  22. Integer right = rightQueue.poll();
  23. // 将数组mid值赋值给节点
  24. int mid = (left + right) / 2;
  25. node.val = nums[mid];
  26. // 处理左边节点
  27. if (left <= mid - 1) {
  28. node.left = new TreeNode(0);
  29. nodeQueue.offer(node.left);
  30. leftQueue.offer(left);
  31. rightQueue.offer(mid - 1);
  32. }
  33. // 处理右边节点
  34. if (right >= mid + 1) {
  35. node.right = new TreeNode(0);
  36. nodeQueue.offer(node.right);
  37. leftQueue.offer(mid + 1);
  38. rightQueue.offer(right);
  39. }
  40. }
  41. return root;
  42. }