2021年1月17日 时间都去哪了?
今日计划:
1.小学初中的辅导。任务已被安排~
2.组会
3.剑指offer的二叉树题目
4.图解tcp ip 的内容
今日工作:
1.小学初中的辅导。任务已被安排~
>2.组会
3.剑指offer的二叉树题目:剑指offer的八道二叉树题目的整理。涉及递归调用+广度优先遍历。
>4.图解tcp ip 的内容
具体内容请点击这里,或者再往下拉一下,复制到下面了。
剑指offer的八道二叉树题目的整理。涉及递归调用+广度优先遍历。
剑指offer的八道二叉树题目的整理。涉及递归调用+广度优先遍历。
剑指offer的八道二叉树题目的整理。涉及递归调用+广度优先遍历。
剑指offer的八道二叉树题目的整理。涉及递归调用+广度优先遍历。
剑指offer的八道二叉树题目的整理。涉及递归调用+广度优先遍历。
剑指offer的八道二叉树题目的整理。涉及递归调用+广度优先遍历。
今日总结: 考虑一天多做几种类型的工作吧,别只刷题。
今日语录:
作者:paceMaK1.r
链接:https://www.zhihu.com/question/65532582/answer/1006046098 来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。朱砂痣美艳活泼,开放,一举一动都撩拨着你的心房,和她在一起有一种刺激的堕落感;白月光是年少时偷偷喜欢的三好学生,纯洁又内向,温柔又理性,你知道白月光是好姑娘,她永远都能在正确的道路上指引你。
和白月光在一起了,你们的生活渐渐平淡如水,她从曾经那个温柔又有学识,不食人间烟火的女孩变成了柴米油盐骂孩子起床的老妈子,你感到窒息,不由得想起年少时碰到的那一抹惊艳,再看看面前这个寡淡的女人,你后悔了。
她成了你胸口的一颗朱砂痣,在你的心头热烈而浓重,让你心跳加速,仿佛自己也年轻了不少。
选择了朱砂痣,那个漂亮外向的女人,她的容颜也会渐渐老去,年龄让她不可能再像一个小丫头一样和你打打闹闹。她变得聒噪,抱怨自己老去的容颜和你那不高的工资。
你们躺在床上,她已沉沉睡去,你还睡不着。你们曾经在这张床上翻云覆雨,哈哈大笑,现在的你却连搂着她睡觉都做不到。
你闭上双眼,感觉眼前一亮,睁开眼,窗外的月光温柔的抚摸着你的脸,你忽然想起学生时代那个红着脸给你讲题的女孩子,她博学多才,温柔可爱,比现在这个聒噪的女人好太多。
你凝视着月光的光束,她就像这个白月光一样,可望而不可即,是你心中永远的痛。
亦或者你根本选择不出来。年少时不管男女都会遇到这样两个完全不同的人,一个激情一个理智,一个爱闹一个平静。朱砂痣刺激你的荷尔蒙,白月光让你回归理智面对现实,但现实也并不是全都是理智组成。
但是,难道全世界的女子真的就只有安静和外向两个性格分别吗?
你从没注意过温婉的她偶尔也喜欢去唱歌喝酒,不小心说一两句脏话便突然害羞,开心起来也会双腿环绕着你的腰抱着你撒娇。
你也从没发现过大大咧咧的她居然喜欢研究宇宙,有时会因为思考活着的意义与人类的未来而惆怅,经常一个人看经典老片而哭的稀里哗啦。
在你的眼中没有完美,所有女人都被分为两类人,朱砂痣?白月光?
你要找的,从来不是一个你真心爱的人,而是一个既能让你充满激情又天真纯洁的人。那不是人,那是幻想。
真心爱一个人,不是朱砂痣也不是白月光,是这些名词之上的,深深爱着的人。
不要以为所有男人心中都有朱砂痣和白月光,他们只有相守一生的人,能分出朱砂痣和白月光的男人,做出什么选择都不是尽善尽美,喝醉后还会和朋友吹嘘年轻时没得到的那个女孩子和黄脸婆的你。
所以,亲爱的男孩啊,别再用这些名词将你对爱人的标准牢牢套住了,这些明明也并不尽善尽美的词汇,何必为自己的终生爱人冠以姓名?
她就是她,哪个也不是,她是你,最爱的人
完。
另一个评论:爱了一个又爱一个,渣的明目张胆,还取名白月光,朱砂痣,两个怕是你都不配拥有吧.
转到2021.1.9-2021.1.31的learning record 首页
明日计划:剑指offer的另外八道二叉树题目的整理。操作系统/计网面经?项目呢?啊…
2.二叉树
- 剑指 Offer 07 .重建二叉树
- 剑指 Offer 26 .树的子结构
- 剑指 Offer 27 .二叉树的镜像
- 剑指 Offer 28 .对称的二叉树
- 剑指 Offer 32 - I 从上到下打印二叉树
- 剑指 Offer 32 - II 从上到下打印二叉树 II
- 剑指 Offer 32 - III 从上到下打印二叉树 III
- 剑指 Offer 33 .二叉搜索树的后序遍历序列
- 剑指 Offer 34 .二叉树中和为某一值的路径
- 剑指 Offer 36 .二叉搜索树与双向链表
- 剑指 Offer 37 .序列化二叉树
- 剑指 Offer 54 .二叉搜索树的第k大节点
- 剑指 Offer 55 - I 二叉树的深度
- 剑指 Offer 55 - II 平衡二叉树
- 剑指 Offer 68 - I 二叉搜索树的最近公共祖先
- 剑指 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 .重建二叉树 【需要重刷】
前序遍历:根→左→右
中序遍历:左→根→右
后序遍历:左→右→根
思考:还是有点难度的。直接去看评论的讲解还是不容易理解的。推荐直接去看视频讲解:
利用了递归
的方法去做的。
前序遍历数组:根→左→右。中序遍历数组:左→根→右
通过遍历中序数组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);
}
}
}