2021-1-17:剑指offer的八道二叉树题目的整理。涉及递归调用+广度优先遍历。

2.二叉树

  1. 剑指 Offer 07 .重建二叉树
  2. 剑指 Offer 26 .树的子结构
  3. 剑指 Offer 27 .二叉树的镜像
  4. 剑指 Offer 28 .对称的二叉树
  5. 剑指 Offer 32 - I 从上到下打印二叉树
  6. 剑指 Offer 32 - II 从上到下打印二叉树 II
  7. 剑指 Offer 32 - III 从上到下打印二叉树 III
  8. 剑指 Offer 33 .二叉搜索树的后序遍历序列
  9. 剑指 Offer 34 .二叉树中和为某一值的路径
  10. 剑指 Offer 36 .二叉搜索树与双向链表
  11. 剑指 Offer 37 .序列化二叉树
  12. 剑指 Offer 54 .二叉搜索树的第k大节点
  13. 剑指 Offer 55 - I 二叉树的深度
  14. 剑指 Offer 55 - II 平衡二叉树
  15. 剑指 Offer 68 - I 二叉搜索树的最近公共祖先
  16. 剑指 Offer 68 - II 二叉树的最近公共祖先

一些功能函数

1.按照前序遍历去打印树。根→左→右

private static void printTree(TreeNode node) {
    System.out.printf("%d ", node.val);
    if (node.left != null) {
        printTree(node.left);
    }
    if (node.right != null) {
        printTree(node.right);
    }
}

2.对于Arrays.copyOfRange的测试用例。

import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        // 对于Arrays.copyOfRange的测试用例
        int[] test = {0, 1, 2, 3, 4, 5, 6};
        int[] array = Arrays.copyOfRange(test, 1, 2);// 只存储了索引为1的元素。
        for (int a : array)
            //array的长度=1a=1
            System.out.println("array的长度=" + array.length + "a=" + a);
    }
}

1.剑指 Offer 07 .重建二叉树 【需要重刷】

前序遍历:→左→右

中序遍历:左→→右

后序遍历:左→右→

思考:还是有点难度的。直接去看评论的讲解还是不容易理解的。推荐直接去看视频讲解:

利用了递归的方法去做的。

image-20210115215955161

前序遍历数组:→左→右。中序遍历数组:左→→右

通过遍历中序数组inorder,得到等于preorder[0](根节点)的索引index,可以将中序遍历数组分开:

中序遍历数组=左子树的中序遍历数组Arrays.copyOfRange(inOrder, 0, index)+根节点

+右子树的中序遍历数组Arrays.copyOfRange(inOrder, index + 1, inOrder.length - 1 + 1))

通过index,以及中序遍历数组:左→→右 的规律,那么左子树的长度为index-0=index。

那么前序遍历数组=根节点+左子树的前序遍历数组Arrays.copyOfRange(preOrder, 1, index + 1)

+右子树的前序遍历数组Arrays.copyOfRange(preOrder, index + 1, preOrder.length - 1 + 1)

import java.util.Arrays;

public class Offer07 {
    public static void main(String[] args) {
        int[] preOrder = {1, 2, 4, 7, 3, 5, 6, 8}; /// 前序遍历
        int[] inOrder = {4, 7, 2, 1, 5, 3, 8, 6}; /// 中序遍历
        TreeNode root = buildTree(preOrder, inOrder);
        printTree(root);
    }

    public static TreeNode buildTree(int[] preOrder, int[] inOrder) {
        // Java中判断一维数组是否为空
        if (preOrder == null || preOrder.length == 0) {
            return null;
        }
        // preOrder 的第一个元素就是根节点。
        TreeNode root = new TreeNode(preOrder[0]);
        int index = findIndex(preOrder, inOrder);
        //root.left=buildTree(左子树的前序数组,左子树的中序数组)
        root.left = buildTree(Arrays.copyOfRange(preOrder, 1, index + 1), Arrays.copyOfRange(inOrder, 0, index));

        //root.right=buildTree(右子树的前序数组,右子树的中序数组)
        root.right = buildTree(Arrays.copyOfRange(preOrder, index + 1, preOrder.length - 1 + 1), Arrays.copyOfRange(inOrder, index + 1, inOrder.length - 1 + 1));

        return root;
    }

    public static int findIndex(int[] preOrder, int[] inOrder) {
        for (int i = 0; i < preOrder.length; i++) {
            if (inOrder[i] == preOrder[0]) {
                return i;
            }
        }
        return 0;
    }

    private static void printTree(TreeNode node) {
        System.out.printf("%d ", node.val);
        if (node.left != null) {
            printTree(node.left);
        }
        if (node.right != null) {
            printTree(node.right);
        }
    }
}
2.剑指 Offer 26 .树的子结构

思考:涉及到递归调用。如果你记住这个题目的做题的思想,那么就容易做出来。具体的思想去看代码吧。

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

空树不是任意一个树的子结构,所以B不为空。非空树不可能是空树的子树,所以A不能为空。

判断B是不是A的子结构:AB完全相等、B是A的左子树的子结构、B是A的右子树的子结构、

AB完全相等:AB头结点值相等,左子树相等,右子树相等。+子树相等=递归调用“AB完全相等”的业务代码

public class Offer26 {
    public static void main(String[] args) {
        TreeNode A = new TreeNode(1);
        A.left = new TreeNode(2);
        A.right = new TreeNode(3);
        printTree(A);
        System.out.println();
        TreeNode B = new TreeNode(1);
        B.left = null;
        B.right = new TreeNode(3);
        printTree(B);
        System.out.println();
        System.out.println(isSubStructure(A, B));
    }

    public static boolean isSubStructure(TreeNode A, TreeNode B) {
        // 输入条件限制
        // 空树不是任意一个树的子结构,所以B不为空。
        // 非空树不可能是空树的子树,所以A不能为空。
        if (A == null || B == null) {
            return false;
        }
        // 判断B是不是A的子结构:AB完全相等、B是A的左子树的子结构、B是A的右子树的子结构、
        return help(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B);

        // ✖ 判断B是不是A的子结构:AB完全相等、A的左子树与B完全相等、A的右子树与B完全相等、
        // ✖ return help(A,B)||help(A.left,B)||help(A.right,B); 
    }

    // 验证AB完全相等,那么AB头结点值相等,左子树相等,右子树相等。
    // 判断子树是否相等,需要递归调用"验证AB完全相等"的功能函数。
    private static boolean help(TreeNode A, TreeNode B) {
        // 递归终止条件 只要B为空,A不管为不为空,B都是A的子结构了,即 A中有出现和B相同的结构和节点值。
        if (B == null) {
            return true;
        }
        if (A == null && B != null) {
            return false;
        }
        // 业务代码
        // 验证AB完全相等,那么AB头结点值相等,左子树相等,右子树相等。
        return A.val == B.val && help(A.left, B.left) && help(A.right, B.right);
    }

    private static void printTree(TreeNode node) {
        System.out.printf("%d ", node.val);
        if (node.left != null) {
            printTree(node.left);
        }
        if (node.right != null) {
            printTree(node.right);
        }
    }
}
3.剑指 Offer 27 .二叉树的镜像

思考:涉及递归调用。输入一个二叉树,该函数输出它的镜像。

public class Offer27 {
    public static void main(String[] args) {
        TreeNode root = new TreeNode(4);
        root.left = new TreeNode(2);
        root.right = new TreeNode(7);
        root.left.left = new TreeNode(1);
        root.left.right = new TreeNode(3);
        root.right.left = new TreeNode(6);
        root.right.right = new TreeNode(9);
        printTree(root);
        System.out.println();
        printTree(mirrorTree(root));
        System.out.println();
    }

    // 二叉树的镜像,也就是左右子树互换。
    public static TreeNode mirrorTree(TreeNode root) {
        // 递归终止条件,
        if (root == null) {
            return null;
        }
        // 这个是c语言中swap交换A,B,两个数据值的模板。
        TreeNode temp = mirrorTree(root.left);
        root.left = mirrorTree(root.right);
        root.right = temp;
        return root;
    }

    private static void printTree(TreeNode root) {
        System.out.print(root.val);
        if (root.left != null) {
            printTree(root.left);
        }
        if (root.right != null) {
            printTree(root.right);
        }
    }
}
4.剑指 Offer 28 .对称的二叉树

思考:涉及到递归调用

public class Offer28 {
    public static void main(String[] args) {
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(2);
        root.left.left = new TreeNode(3);
        root.left.right = new TreeNode(4);
        root.right.left = new TreeNode(4);
        root.right.right = new TreeNode(3);
        printTree(root);
        System.out.println();
        System.out.println(isSymmetric(root));
    }

    // 用来判断一棵二叉树是不是对称的
    public static boolean isSymmetric(TreeNode root) {
        // 空树肯定是镜像对称
        if (root == null) {
            return true;
        }
        // help,业务代码,用于判断左右子树是否对称
        return help(root.left, root.right);
    }

    public static boolean help(TreeNode left, TreeNode right) {
        //递归终止条件
        if (left == null && right == null) {
            return true;
        }
        // 上面已经去除了同时为空的情况,如果两者不同时为空,那么不可能镜像对称,则返回false
        if (left == null || right == null) {
            return false;
        }
        // left.val==right.val && left.left=right.right && left.right=right.left
        return left.val == right.val && help(left.left, right.right) && help(left.right, right.left);
    }


    private static void printTree(TreeNode root) {
        System.out.print(root.val);
        if (root.left != null) {
            printTree(root.left);
        }
        if (root.right != null) {
            printTree(root.right);
        }
    }
}
5.剑指 Offer 32 - I 从上到下打印二叉树 【需要重刷】

ArrayList如何转换为int[]数组 https://blog.csdn.net/huanghanqian/article/details/73920439
这个是java8的特性
int[] res = list.stream().mapToInt(Integer::intValue).toArray();

思考:从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。这道题是:二叉树的宽度优先搜索

【需要重刷做法2+做法3。做法1是错误的。】

其实这题很明显就是二叉树的宽度优先搜索,

(1)递归调用的一种错误解法

下面贴出一个错误的解法,并思考这么做为什么错了。

这里用到的递归调用的思想是,添加左右子树的值→递归调用左子树(添加左子树的左右子树的值)→递归调用右子树(添加右子树的左右子树的值)

前序遍历输出为:1->2->4->8->9->5->10->11->3->6->12->13->7->14->15

错误解法返回的答案为:[1, 2, 3, 4, 5, 8, 9, 10, 11, 6, 7, 12, 13, 14, 15]。45应该接67,但是没有接,是因为他没有遍历完一层的数据,就进入了下一层,然后再返回来的。

错误来源是:help(root.left, list); // 递归调用左子树(添加左子树的左右子树的值)
help(root.right, list);// 递归调用右子树(添加右子树的左右子树的值)
这样会先遍历完左子树,再去遍历右子树。

修改目标,每次先去遍历完每一层的数据。

import java.util.ArrayList;

// [0,2,4,1,null,3,-1,5,1,null,6,null,8]
public class Offer32_1_1 {
    public static void main(String[] args) {
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(3);
        root.left.left = new TreeNode(4);
        root.left.right = new TreeNode(5);
        root.right.left = new TreeNode(6);
        root.right.right = new TreeNode(7);

        root.left.left.left = new TreeNode(8);
        root.left.left.right = new TreeNode(9);
        root.left.right.left = new TreeNode(10);
        root.left.right.right = new TreeNode(11);
        root.right.left.left = new TreeNode(12);
        root.right.left.right = new TreeNode(13);
        root.right.right.left = new TreeNode(14);
        root.right.right.right = new TreeNode(15);

        printTree(root);
        System.out.println();
        //System.out.println(levelOrder(root));
        //直观性的输出
        System.out.println(levelOrder2(root));
    }

    // 返回数组,那么可以先用链表去存数据,到最后再将链表转为数组
    // 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印
    public static int[] levelOrder(TreeNode root) {
        // 首先,判断输入是否为空
        if (root == null) {
            return new int[0];
        }
        ArrayList<Integer> list = new ArrayList<>();
        list.add(root.val);
        help(root, list);
        int[] res = list.stream().mapToInt(Integer::intValue).toArray();
        return res;
    }

    public static ArrayList<Integer> levelOrder2(TreeNode root) {
        // 首先,判断输入是否为空
        if (root == null) {
            return new ArrayList<>();
        }
        ArrayList<Integer> list = new ArrayList<>();
        list.add(root.val);
        help(root, list);
        return list;
    }

    // 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
    // 做法类似于树的前序,中序,后序遍历,只不过之前是打印,现在是向链表里面添加元素
    // 递归调用的思想是,添加左右子树的值→递归调用左子树(添加左子树的左右子树的值)→递归调用右子树(添加右子树的左右子树的值)

    private static void help(TreeNode root, ArrayList<Integer> list) {
        System.out.println(list);
        // 这个是递归调用的终止条件
        if (root == null) {
            return;
        }
        // 节点为空,则自动跳过
        // 添加左右子树的值
        if (root.left != null) {
            list.add(root.left.val);
        }
        if (root.right != null) {
            list.add(root.right.val);
        }
        // 递归调用左子树(添加左子树的左右子树的值)
        help(root.left, list);
        // 递归调用右子树(添加右子树的左右子树的值)
        help(root.right, list);
    }

    private static void printTree(TreeNode root) {
        System.out.print(root.val+"->");
        if (root.left != null) {
            printTree(root.left);
        }
        if (root.right != null) {
            printTree(root.right);
        }
    }
}
(2)递归调用【需要重刷】

思考:利用height,起到一个定位的作用。

下面代码的输出结果如下:

1->2->4->8->9->5->10->11->3->6->12->13->7->14->15->
height=0 list=[[]]
height=1 list=[[1], []]
height=2 list=[[1], [2], []]
height=3 list=[[1], [2], [4], []]
height=3 list=[[1], [2], [4], [8]]
height=2 list=[[1], [2], [4], [8, 9]]

height=3 list=[[1], [2], [4, 5], [8, 9]]
height=3 list=[[1], [2], [4, 5], [8, 9, 10]]
height=1 list=[[1], [2], [4, 5], [8, 9, 10, 11]]
height=2 list=[[1], [2, 3], [4, 5], [8, 9, 10, 11]]

height=3 list=[[1], [2, 3], [4, 5, 6], [8, 9, 10, 11]]
height=3 list=[[1], [2, 3], [4, 5, 6], [8, 9, 10, 11, 12]]
height=2 list=[[1], [2, 3], [4, 5, 6], [8, 9, 10, 11, 12, 13]]

height=3 list=[[1], [2, 3], [4, 5, 6, 7], [8, 9, 10, 11, 12, 13]]
height=3 list=[[1], [2, 3], [4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14]]

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

import java.util.ArrayList;
import java.util.List;

// [0,2,4,1,null,3,-1,5,1,null,6,null,8]
public class Offer32_1_2 {
    public static void main(String[] args) {
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(3);
        root.left.left = new TreeNode(4);
        root.left.right = new TreeNode(5);
        root.right.left = new TreeNode(6);
        root.right.right = new TreeNode(7);

        root.left.left.left = new TreeNode(8);
        root.left.left.right = new TreeNode(9);
        root.left.right.left = new TreeNode(10);
        root.left.right.right = new TreeNode(11);
        root.right.left.left = new TreeNode(12);
        root.right.left.right = new TreeNode(13);
        root.right.right.left = new TreeNode(14);
        root.right.right.right = new TreeNode(15);

        printTree(root);
        System.out.println();
        // System.out.println(levelOrder(root));
        // 直观性的输出
        System.out.println(levelOrder2(root));
    }

    public static ArrayList<Integer> levelOrder2(TreeNode root) {
        ArrayList<ArrayList<Integer>> list = new ArrayList<>();
        levelHelper(list, root, 0);
        ArrayList<Integer> tempList = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            tempList.addAll(list.get(i));
        }
        return tempList;
    }

    // 返回数组,那么可以先用链表去存数据,到最后再将链表转为数组
    // 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印
    public static int[] levelOrder(TreeNode root) {
        ArrayList<ArrayList<Integer>> list = new ArrayList<>();
        levelHelper(list, root, 0);
        // 二维列表转为一维列表
        ArrayList<Integer> tempList = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            tempList.addAll(list.get(i));
        }
        //把list转化为数组
//        int[] res = new int[tempList.size()];
//        for (int i = 0; i < tempList.size(); i++) {
//            res[i] = tempList.get(i);
//        }
//        return res;
        int[] res = tempList.stream().mapToInt(Integer::intValue).toArray();
        return res;
    }

    // levelHelper 是主要的业务代码
    public static void levelHelper(ArrayList<ArrayList<Integer>> list, TreeNode root, int height) {
        // 判断输入节点是否为空
        if (root == null) {
            return;
        }
        // >= 中的等于,应该是做初始化的
        if (height >= list.size()) {
            list.add(new ArrayList<>());
        }
        // height起到了一个定位的作用
        System.out.println("height=" + height + " list=" + list);
        list.get(height).add(root.val);
        levelHelper(list, root.left, height + 1);
        levelHelper(list, root.right, height + 1);
    }

    private static void printTree(TreeNode root) {
        System.out.print(root.val + "->");
        if (root.left != null) {
            printTree(root.left);
        }
        if (root.right != null) {
            printTree(root.right);
        }
    }
}
(3)二叉树的宽度优先搜索【需要重刷】

思考:二叉树的宽度优先搜索的题解:+建议先看视频讲解:

宽度优先搜索=层序遍历=利用队列queue去做。

思路:从队列queue中取出一个节点,并将其左右节点加入队列queue。

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

// [0,2,4,1,null,3,-1,5,1,null,6,null,8]
public class Offer32_1_3 {
    public static void main(String[] args) {
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(3);
        root.left.left = new TreeNode(4);
        root.left.right = new TreeNode(5);
        root.right.left = new TreeNode(6);
        root.right.right = new TreeNode(7);

        root.left.left.left = new TreeNode(8);
        root.left.left.right = new TreeNode(9);
        root.left.right.left = new TreeNode(10);
        root.left.right.right = new TreeNode(11);
        root.right.left.left = new TreeNode(12);
        root.right.left.right = new TreeNode(13);
        root.right.right.left = new TreeNode(14);
        root.right.right.right = new TreeNode(15);

        printTree(root);
        System.out.println();
        // System.out.println(levelOrder(root));
        // 直观性的输出
        System.out.println(levelOrder2(root));
    }

    // 返回数组,那么可以先用链表去存数据,到最后再将链表转为数组
    // 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印
    public static int[] levelOrder(TreeNode root) {
        if (root == null) {
            return new int[0];
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        List<Integer> list = new ArrayList<>();
        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            list.add(node.val);

            if (node.left != null) {
                queue.add(node.left);
            }
            if (node.right != null) {
                queue.add(node.right);
            }
        }
        int[] res = list.stream().mapToInt(Integer::intValue).toArray();
        return res;
    }

    public static List<Integer> levelOrder2(TreeNode root) {
        if (root == null) {
            return new ArrayList<>();
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        List<Integer> list = new ArrayList<>();
        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            list.add(node.val);

            if (node.left != null) {
                queue.add(node.left);
            }
            if (node.right != null) {
                queue.add(node.right);
            }
        }
        return list;
    }

    private static void printTree(TreeNode root) {
        System.out.print(root.val + "->");
        if (root.left != null) {
            printTree(root.left);
        }
        if (root.right != null) {
            printTree(root.right);
        }
    }
}
6.剑指 Offer 32 - II 从上到下打印二叉树 II
(1)递归调用

思考:按照递归调用的方法。解法和32 - I 的解法(2)一模一样。

里面的一些小细节要非常注意哈

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

// [0,2,4,1,null,3,-1,5,1,null,6,null,8]
public class Offer32_2 {
    public static void main(String[] args) {
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(3);
        root.left.left = new TreeNode(4);
        root.left.right = new TreeNode(5);
        root.right.left = new TreeNode(6);
        root.right.right = new TreeNode(7);

        root.left.left.left = new TreeNode(8);
        root.left.left.right = new TreeNode(9);
        root.left.right.left = new TreeNode(10);
        root.left.right.right = new TreeNode(11);
        root.right.left.left = new TreeNode(12);
        root.right.left.right = new TreeNode(13);
        root.right.right.left = new TreeNode(14);
        root.right.right.right = new TreeNode(15);

        printTree(root);
        System.out.println();
        System.out.println(levelOrder(root));
    }

    public static List<List<Integer>> levelOrder(TreeNode root) {
        if (root == null) {
            return new ArrayList<>();
        }
        List<List<Integer>> res = new ArrayList<>();
        help(root, res, 0);
        return res;
    }

    private static void help(TreeNode root, List<List<Integer>> res, int height) {
        if (root == null) {
            return;
        }
        if (height >= res.size()) {
            res.add(new ArrayList<>());
        }
        res.get(height).add(root.val);
        if (root.left != null) {
            help(root.left, res, height + 1);
        }
        if (root.right != null) {
            help(root.right, res, height + 1);
        }
    }

    private static void printTree(TreeNode root) {
        System.out.print(root.val + "->");
        if (root.left != null) {
            printTree(root.left);
        }
        if (root.right != null) {
            printTree(root.right);
        }
    }
}
(2)广度优先搜索 从上到下打印二叉树 II 【需要重刷】

你多看几遍这道题,仔细考虑for里面为啥这么写。就能更加深刻的去理解广度优先遍历了。

思想:从队列中取出一个节点,向队列中存入该节点的左右节点。

这里和上面一道题目的区别是,上一题直接返回即可,这道题每一层就需要添加到一个链表里了。

实际输出一下,为什么会出错,将i++换成i--,两种程序的状态是?+下面内容进行解释:

// 易错点:
// 这里的for如果写反了,如果写成i++,那就错了。
for (int i = queue.size(); i > 0; i--)
for (int i = 0; i < queue.size(); i++)
// 应该是这样说,在for循环中,queue.size()的大小随着queue不断的去添加元素,其大小是变化的,所以i < queue.size()的边界是动态的。
// 如果先去用 for (int i = queue.size(); i > 0; i--) ,那么i > 0的边界是确定的,所以不会输出错误的结果。
    
// 更应该思考的是:queue 是存一层的元素,然后 poll 吐出来,存到链表 list 里面。
// 与此同时,去存储树的左右节点到队列 queue 中
// 由于队列的先入先出的特性,所以先去输出上一层的元素,再去输出下一层的元素。
// 正确的答案:
1->2->4->8->9->5->10->11->3->6->12->13->7->14->15->
i = 1 queue.size() = 2 temp = [1]

i = 2 queue.size() = 3 temp = [2]
i = 1 queue.size() = 4 temp = [2, 3]

i = 4 queue.size() = 5 temp = [4]
i = 3 queue.size() = 6 temp = [4, 5]
i = 2 queue.size() = 7 temp = [4, 5, 6]
i = 1 queue.size() = 8 temp = [4, 5, 6, 7]

i = 8 queue.size() = 7 temp = [8]
i = 7 queue.size() = 6 temp = [8, 9]
i = 6 queue.size() = 5 temp = [8, 9, 10]
i = 5 queue.size() = 4 temp = [8, 9, 10, 11]
i = 4 queue.size() = 3 temp = [8, 9, 10, 11, 12]
i = 3 queue.size() = 2 temp = [8, 9, 10, 11, 12, 13]
i = 2 queue.size() = 1 temp = [8, 9, 10, 11, 12, 13, 14]
i = 1 queue.size() = 0 temp = [8, 9, 10, 11, 12, 13, 14, 15]

[[1], [2, 3], [4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14, 15]] 
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

// [0,2,4,1,null,3,-1,5,1,null,6,null,8]
public class Offer32_2_2 {
    public static void main(String[] args) {
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(3);
        root.left.left = new TreeNode(4);
        root.left.right = new TreeNode(5);
        root.right.left = new TreeNode(6);
        root.right.right = new TreeNode(7);

        root.left.left.left = new TreeNode(8);
        root.left.left.right = new TreeNode(9);
        root.left.right.left = new TreeNode(10);
        root.left.right.right = new TreeNode(11);
        root.right.left.left = new TreeNode(12);
        root.right.left.right = new TreeNode(13);
        root.right.right.left = new TreeNode(14);
        root.right.right.right = new TreeNode(15);

        printTree(root);
        System.out.println();
        System.out.println(levelOrder(root));
    }

    public static List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        List<List<Integer>> res = new ArrayList<>();
        if (root != null) {
            queue.add(root);
        }
        while (!queue.isEmpty()) {
            // 每次新建一个 list 去存储每一行的内容
            List<Integer> temp = new ArrayList<>();
            // 这里的for如果写反了,如果写成i++,那就错了。
            for (int i = queue.size(); i > 0; i--) {
//            for (int i = 0; i < queue.size(); i++) {
                TreeNode node = queue.poll();
                temp.add(node.val);
                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
                System.out.println("i = " + i + " queue.size() = " + queue.size() + " temp = " + temp);
            }
            System.out.println();
            res.add(temp);
        }
        return res;
    }

    private static void printTree(TreeNode root) {
        System.out.print(root.val + "->");
        if (root.left != null) {
            printTree(root.left);
        }
        if (root.right != null) {
            printTree(root.right);
        }
    }
}
7.剑指 Offer 32 - III 从上到下打印二叉树 III
(1)递归调用

思考:递归调用,通过高度 height 来控制存储每一层的元素。

没想到合适的做法。明天再想吧。。。主要是这个三份“从上到下打印二叉树”做的脑袋都大了。迷。
(2)广度优先遍历

思考:相对于从上到下打印二叉树 II的变化之处是在向每一层的链表中添加元素,按照奇偶性去选择向链表的首部添加元素,还是向链表的末尾添加元素。

这里借用了LinkedList 双向链表 +双向链表的功能。

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

// [0,2,4,1,null,3,-1,5,1,null,6,null,8]
public class Offer32_3 {
    public static void main(String[] args) {
        TreeNode root = new TreeNode(1);
        root.left = new TreeNode(2);
        root.right = new TreeNode(3);
        root.left.left = new TreeNode(4);
        root.left.right = new TreeNode(5);
        root.right.left = new TreeNode(6);
        root.right.right = new TreeNode(7);

        root.left.left.left = new TreeNode(8);
        root.left.left.right = new TreeNode(9);
        root.left.right.left = new TreeNode(10);
        root.left.right.right = new TreeNode(11);
        root.right.left.left = new TreeNode(12);
        root.right.left.right = new TreeNode(13);
        root.right.right.left = new TreeNode(14);
        root.right.right.right = new TreeNode(15);

        printTree(root);
        System.out.println();
        System.out.println(levelOrder(root));
    }

    public static List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            LinkedList<Integer> temp = new LinkedList<>();
            for (int i = queue.size(); i > 0; i--) {
                TreeNode node = queue.poll();
                if (res.size() % 2 == 0) {
                    temp.addLast(node.val);
                } else {
                    temp.addFirst(node.val);
                }
                if (node.left != null) {
                    queue.add(node.left);
                }
                if (node.right != null) {
                    queue.add(node.right);
                }
            }
            res.add(temp);
        }
        return res;
    }

    private static void printTree(TreeNode root) {
        System.out.print(root.val + "->");
        if (root.left != null) {
            printTree(root.left);
        }
        if (root.right != null) {
            printTree(root.right);
        }
    }
}
8.剑指 Offer 33 .二叉搜索树的后序遍历序列 【需要重刷】

二叉搜索树,它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。

您还没任何提交记录。哈哈。竟然没做过这个题目~

思考:单纯去看评论中的讲解是不容易理解的,点击这里去看视频讲解:

使用递归操作去求解问题。只是参考了视频中的解法。评论中大神们的做法还没有去深究。

import java.util.ArrayList;
import java.util.List;

public class Offer33 {
    public static void main(String[] args) {
//        int[] postorder = new int[]{1, 6, 3, 2, 5};
        int[] postorder = new int[]{1, 3, 2, 6, 5};
        System.out.println(verifyPostorder(postorder));
    }

    public static boolean verifyPostorder(int[] postorder) {
        List<Integer> list = new ArrayList<>();
        // 二叉搜索树,它或者是一棵空树,或者是具有下列性质的二叉树
        if (postorder == null || postorder.length == 0) {
//            return false;
            return true;
        }
        // 为了便于操作。作者将数组转为链表
        for (int i = 0; i < postorder.length; i++) {
            list.add(postorder[i]);
        }
        return verifyBST(list);
    }

    // 递归操作
    public static boolean verifyBST(List<Integer> postorder) {
        if (postorder.size() < 1) {
            return true;
        }
        int len = postorder.size();
        // 后序遍历:左→右→根。根在数组/链表的末尾
        // get 是从 0 开始的。
        int rootVal = postorder.get(len - 1);
        List<Integer> leftList = new ArrayList<>();
        List<Integer> rightList = new ArrayList<>();
        int i = 0;
        // 为了便于操作。作者将数组转为链表。便利之处在这里,容易进行动态添加元素。
        // 左子树
        while (postorder.get(i) < rootVal) {
            // get(i++) 是先去获得get(i),然后i执行➕1的操作
            leftList.add(postorder.get(i++));
        }
        // 为了便于操作。作者将数组转为链表。便利之处在这里,容易进行动态添加元素。
        // 右子树
        while (postorder.get(i) > rootVal) {
            // get(i++) 是先去获得get(i),然后i执行➕1的操作
            rightList.add(postorder.get(i++));
        }
        if (i < postorder.size() - 1) {
            return false;
        }
        return verifyBST(leftList) && verifyBST(rightList);
    }

    private static void printTree(TreeNode root) {
        System.out.print(root.val);
        if (root.left != null) {
            printTree(root.left);
        }
        if (root.right != null) {
            printTree(root.right);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值