第7节 继续二叉树的很多题目
##这是数据结构与算法新手班-左程云第七节课的笔记##
上一节课不是留了很多题吗?留在这节课讲了。
题目一:二叉树按层遍历并收集结点
这个思路我们想了一下,就先把每一层的结点装进来,然后弹出这一层的结点,遍历这一层的每个结点,然后把它的下一层孩子都扔进队列中。如此反复直到队列为空。
那么,什么队列效率最高呢?我们进行了ArrayList和LinkedList的效率对比,结果发现,LinkedList效率明显高于ArrayList。
import java.util.ArrayList;
import java.util.LinkedList;
public class Test {
public static void main(String[] args) {
int testTime = 100000;
long start;
long end;
System.out.println("=====");
// ArrayList装载值
ArrayList<Integer> arr1 = new ArrayList<>();
start = System.currentTimeMillis();
for(int i = 0;i < testTime;i++){
arr1.add(0,i);
}
end = System.currentTimeMillis();
System.out.println(end - start);
// LinkedList装载值
LinkedList<Integer> arr2 = new LinkedList<>();
start = System.currentTimeMillis();
for(int i = 0 ;i < testTime;i++){
arr2.add(0,i);
}
end = System.currentTimeMillis();
System.out.println(end - start);
}
}
现在开始做题了!
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class Test {
public static class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val) {
this.val = val;
}
}
// 二叉树按层遍历并收集结点
// 思路:
// 第一步:拿出此时队列的size,size有多少,就进行多少次第二步
// 第二步:弹出结点,先左后右
public List<List<Integer>> levelOrderBottom(TreeNode root){
List<List<Integer>> ans = new LinkedList<>();
if(root == null){
return ans;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
int size = queue.size();
List<Integer> curAns = new LinkedList<>();
for(int i = 0 ; i < size;i++){
TreeNode curNode = queue.poll();
curAns.add(curNode.val);
if(curNode.left != null){
queue.add(curNode.left);
}
if(curNode.right != null){
queue.add(curNode.right);
}
}
ans.add(0,curAns);
}
return ans;
}
public static void main(String[] args) {
}
}
扩展:在Java里面,栈的实现比较慢,我们可以利用LinkedList来实现栈的功能。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class Test {
public static void main(String[] args) {
// 利用LinkedList来实现栈的功能
LinkedList<Integer> s = new LinkedList<>();
s.addLast(1);
s.addLast(2);
s.addLast(3);
while(!s.isEmpty()){
System.out.println(s.pollLast());
}
// 如果明确知道长度不大于100,可以利用数组来实现栈的功能,这个是最快的
int[] stack = new int[100];
int index = 0;
stack[index++] = 1;
stack[index++] = 2;
stack[index++] = 3;
// 弹出怎么办呢?
System.out.println(stack[--index]);
System.out.println(stack[--index]);
System.out.println(stack[--index]);
}
}
题目二:判断是否为平衡二叉树
什么是平衡二叉树?每一棵子树的 |左树的高度-右树的高度| ≤ 1
// 测试链接:https://leetcode.com/problems/balanced-binary-tree
public class Code02_BalancedBinaryTree {
public static class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
TreeNode(int val) {
this.val = val;
}
}
public static class Info {
public boolean isBalanced;
public int height;
public Info(boolean i, int h) {
isBalanced = i;
height = h;
}
}
public static boolean isBalanced(TreeNode root) {
return process(root).isBalanced;
}
public static Info process(TreeNode root) {
// root == null
if (root == null) {
return new Info(true, 0);
}
// root != null
Info leftInfo = process(root.left);
Info rightInfo = process(root.right);
int height = Math.max(leftInfo.height, rightInfo.height) + 1;
boolean isBalanced = leftInfo.isBalanced && rightInfo.isBalanced
&& Math.abs(leftInfo.height - rightInfo.height) < 2;
return new Info(isBalanced, height);
}
}
题目三:判断是否为搜索二叉树
二叉查找树(Binary Search Tree):它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势;所以应用十分广泛,例如在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作。
思路:第一个方法:对于搜索二叉树而言,中序遍历的结果就是严格递增的;
// 这个简单,直接判断就行
第二个方法:递归。
// 测试链接:https://leetcode.com/problems/balanced-binary-tree
public class Code02_IsBinarySearchTree {
public static class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
TreeNode(int val) {
this.val = val;
}
}
public static class Info {
public boolean isBST;
public int max;
public int min;
public Info(boolean is, int max, int min) {
isBST = is;
this.max = max;
this.min = min;
}
}
public static boolean IsBinarySearchTree(TreeNode root) {
return process(root).isBST;
}
public static Info process(TreeNode root) {
// root == null
if (root == null) {
// 可以返回0吗?不行,假如root的值是负数呢?想一下可以吗?
return null;
}
// root != null
Info leftInfo = process(root.left);
Info rightInfo = process(root.right);
// 假如上边有返回null的,这个地方就有压力了,你得解决null的情况呀!
int max = root.val;
int min = root.val;
if (leftInfo != null) {
max = Math.max(leftInfo.max, max);
min = Math.max(leftInfo.min, min);
}
if (rightInfo != null) {
max = Math.max(rightInfo.max, max);
min = Math.max(rightInfo.min, min);
}
// 首先我们默认为true;然后我们把所有违规的都列全,赋值为false,最后统一返回就可以。
// 这些情况有哪些呢?
// 1. 左树或右树有值的情况:看左树和右树是否满足搜索树的条件、看值是否符合条件。
// 2. 左树和右树都是null,直接返回true。
boolean isBST = true;
if (leftInfo != null && !leftInfo.isBST) {
isBST = false;
}
if (rightInfo != null && !leftInfo.isBST) {
isBST = false;
}
// 判读左树的最大值是否小于root的值。
boolean leftMaxLessX = leftInfo == null ? true : (leftInfo.max < root.val);
// 判读右树的最小值是否大于root的值。
boolean rightMinMoreX = leftInfo == null ? true : (rightInfo.min > root.val);
if (!(leftMaxLessX && rightMinMoreX)) {
isBST = false;
}
return new Info(isBST, max, min);
}
}
题目二:判断是否为平衡搜索二叉树
这个就不写了,上面的你可以调两次判断,然后一与就完事了~
题目三:能否组成路径和
给定一个数值,判断是否存在一条路径(从根结点到叶子结点,从头贯穿到最下面)?什么样的路径呢?就是将路径上所有的结点值相加后,看能否等于给定的数值。
public class Test {
// 测试链接:https://leetcode.com/problems/path-sum
public static class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
TreeNode(int val) {
this.val = val;
}
}
// 全局公共变量
public static boolean isSum = false;
public static boolean hasPathSum(TreeNode root, int sum) {
if (root == null) {
return false;
}
// 因为是全局变量,如果本棵树是true,那么下次输入一棵新树的时候,应该先置为false。
isSum = false;
process(root, 0, sum);
return isSum;
}
// 之前已经形成的和定义为preSum,最终想要的目标叫sum
public static void process(TreeNode x, int preSum, int sum) {
// 当x为叶结点的时候
if (x.left == null && x.right == null) {
if (x.val + preSum == sum) {
isSum = true;
}
return;
}
// x是非叶节点
preSum += x.val;
if (x.left != null) {
process(x.left, preSum, sum);
}
if (x.right != null) {
process(x.right, preSum, sum);
}
}
}
到现在对递归的体会就是:要想做递归,你就必须得抽象出一个公共操作,并且用code把这个公共操作coding出来,此外,应该额外注意边界情况。以上述递归而言,这个方法的功能就是将preSum和输入结点的值相加,并看是不是答案,并且向下一层运行,直到叶子结点。
题目四:收集达标路径和
// 这个得重新到实验室做一下!