文章目录
引言
- 二叉搜索树一直是我的弱项,今明两天把做过的二叉树复习一下,然后周二之前,把所有的二叉树都做完,把这个章节给过了!
复习
二叉树的中序遍历
下述为递归实现
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
List<Integer> list;
public void midOrder(TreeNode root){
if(root == null) return;
if(root.left != null) midOrder(root.left);
list.add(root.val);
if(root.right != null) midOrder(root.right);
}
public List<Integer> inorderTraversal(TreeNode root) {
list = new ArrayList<>();
midOrder(root);
return list;
}
}
迭代实现
- 这里使用循环实现,本质上,还是使用栈去模仿函数调用,所以,需要创建一个栈!
- 然后对照着dfs,每一次递归都是一次入栈操作,具体实现如下
说实话,这个迭代实现的代码并不好记忆,讲当前节点的左子树遍历到叶子节点,如果还为空,就弹出对应的栈顶元素,加入栈中,然后遍历到右子树
还是背一下吧,多练几遍,整理在我的笔记上!
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
Deque<TreeNode> stk = new ArrayDeque<>();
while(root != null || !stk.isEmpty()){
// 向左进行循环遍历,将所有左子节点遍历完毕,
while(root != null){
stk.push(root);
root = root.left;
}
root = stk.pop();
list.add(root.val);
root = root.right;
}
return list;
}
}
二叉树的最大深度
- 第一次学习连接
- 题目链接
- 这个题目是第二次做,首先明白最大深度是从根节点出发,然后找到叶子节点的最远路径,这个应该怎么做?可以使用递归去解决!
- 找到每一个子树的最大深度,然后返回
- 每一个子树的最大深度应该是左子树和右子树的深度需要做一个比较,然后返回,具体实现如下
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
int leftDept = maxDepth(root.left);
int rightDept = maxDepth(root.right);
return Math.max(leftDept,rightDept) + 1;
}
}
广度优先实现==》交换队列
- 无论是计算地图的BFS还是计算深度,都是使用这种类似的双队列交换的方式,现在还能想起来使用BFS如何计算地图的最短路径吗?
- 需要单独创建对应的矩阵进行保存一下!
class Solution {
public int maxDepth(TreeNode root) {
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
int res = 0;
while(!q.isEmpty()){
int nums = q.size();
while(nums > 0){
TreeNode temp = q.peek();
q.poll();
if(temp.left != null) q.offer(temp.left);
if(temp.right != null) q.offer(temp.right);
nums --;
}
res ++;
}
return res;
}
}
二叉树的直径
-
这个题目第一次做,就全部都是坑,想了半天,还是看答案才实现的,第二次做,看看咋样吧!
-
直径==》就是最大路径,相邻最远的两个节点,这里需要比较一下每一个节点的左子树和右子树的最大深度,然后的最终的结果就是
- 左子树的深度 + 右子树的深度 + 1
class Solution {
int len = 0;
public int dfs(TreeNode root){
if(root == null) return 0;
int l = dfs(root.left);
int r = dfs(root.right);
len = Math.max(l + r + 1,len);
return Math.max(l ,r) + 1;
}
public int diameterOfBinaryTree(TreeNode root) {
// 正常的就是单独的深度
dfs(root);
return len - 1;
}
}
这里要注意是节点的数量,并不是边的数量,最后需要减一
优化:使用树形DP实现
- 任取一点作为起点,找到距离该点最远的一个点u
- 再找到距离u最远的一个点v
- u和v之间的距离就是直径
不知道说什么,两个月之前,你敢想,这个题目我做了连续四天,最后一篇博客上还加了一句,这个题目还是蛮简单的,只要懂了思路,我靠,现在我居然看不懂我写的东西!是我写的吗?我在哪里啊?
这里还是单独整理一个系列的章节,再来看这个问题!
整理了才发现,这题不能用动态规划,除非这个树的指针是双向指针,这里是单向指针,是一个二叉树,并不能通过子节点获取父节点,所以只能使用这种方法!!
二叉树的层序遍历
这个题目应该是使用两个队列交替实现
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> q1 = new LinkedList<>();
Queue<TreeNode> q2 = new LinkedList<>();
q1.offer(root);
List<List<Integer>> res= new ArrayList<>();
if(root == null) return res;
while(!q1.isEmpty()){
List<Integer> list = new ArrayList<>();
while(!q1.isEmpty()){
TreeNode node = q1.poll();
list.add(node.val);
if(node.left != null) q2.offer(node.left);
if(node.right != null) q2.offer(node.right);
}
res.add(new ArrayList(list));
Queue<TreeNode> temp = q1;
q1 = q2;
q2 = temp; // 写成了q2 = q1 整了半天
}
return res;
}
}
这里整了差不多十五分钟,十分钟写完,但是没看出问题,发现了,是最底下的交换搞错了!难顶!
将有序数组转换为二叉搜索树
这个题目第一次就没会做,当时对于二叉搜索树有种恐惧,因为忘记的太多了,以前还记得怎么左旋右旋,怎么平衡,现在都忘记了!
但是这个题目比较特殊,已经排好序了,而且是构建平衡二叉树,可以直接找中点
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
TreeNode dfs(int[] nums , int l,int r){
if(l > r) return null;
int midIdx = (l + r) >> 1;
TreeNode root = new TreeNode(nums[midIdx]);
root.left = dfs(nums,l,midIdx - 1);
root.right = dfs(nums,midIdx + 1,r);
return root;
}
public TreeNode sortedArrayToBST(int[] nums) {
return dfs(nums,0,nums.length - 1);
}
}
验证二叉搜索树
- 题目链接
- 这个题目并没有找到之前的连接,但是题目记录上显示已经做过了,想来是做过了,但是没有记录下来吧!今天好好做一下!
注意
- 判断是否为有效的二叉搜索树,有效返回true,无效返回false
- 二叉搜索树定义
- 左子树只包含小于当前节点的树===>左子树的最大值不能大于根节点
- 右子树只包含大于当前节点的树===》右子树的最小值不能大于根节点
- 左右子树都是二叉搜索树
个人实现
- 粗浅的看,应该是递归
- 终止条件:返回空节点,直接是true
- 迭代内容:先判定左右子节点,然后在判定左右子树是不是?
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
boolean dfs(TreeNode root,List<Integer> big,List<Integer> smal){
if(root == null) return true;
if(root.left != null){
for(int i :big) if()
if(root.left.val >= root.val) return false;
if(!isValidBST(root.left)) return false;
}
if(root.right != null){
if(root.right.val <= root.val) return false;
if(!isValidBST(root.right)) return false;
}
}
public boolean isValidBST(TreeNode root) {
List<Integer> big = new ArrayList<>(root.val);
List<Integer> smal = new ArrayList<>(root.val);
return dfs(root,big,smal);
}
}
错误分析
- 这里没改出来,会出现如下情况,并不知道怎么处理!
- 因为从上往下传递最小值,明显不现实,而且有很多最小值,而且还涉及到还原现场!这个情况并不知道怎么处理!
参考实现
- 这里就很好的实现了,使用一个范围,本质上,不断和最小值比较,根本上就是一个比较范围的问题!
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
boolean dfs(TreeNode root,long lower,long upper){
if(root == null) return true;
int val = root.val;
System.out.println(1);
if(val <= lower || val >= upper) return false;
System.out.println(1);
if(!dfs(root.left,lower,val)) return false;
if(!dfs(root.right,val,upper)) return false;
return true;
}
public boolean isValidBST(TreeNode root) {
long lower = Long.MIN_VALUE;
long upper = Long.MAX_VALUE;
return dfs(root,lower,upper);
}
}
这个得好好记忆一下,因为我刚才纠结的是一个很常见的问题,这种递归问题是怎么解决的!
二叉搜索树中第K小的元素
- 230 二叉搜索树中第K小的元素
注意 - 节点数量可能为1
- 按照排序从前往后找第K小的数字
- 如何优化?频繁查找和插入第K小的数字,如何优化
个人实现
- 我对于二叉树的性质了解的并不多,这个题目并没有什么很好的办法,所以这里可能会通过前序遍历,保存所有的节点,然后再返回对应的序列!
- 前序遍历二叉搜索树是从小到大
class Solution {
List<Integer> list;
void preOrder(TreeNode root){
if(root == null) return ;
preOrder(root.left);
list.add(root.val);
preOrder(root.right);
}
public int kthSmallest(TreeNode root, int k) {
list = new ArrayList<>();
preOrder(root);
System.out.println(list.toString());
return list.get(k - 1);
}
}
参考实现
跟我相同的思路,不过他并没有完整的遍历整个树,然后在访问对应的数组,而是在遍历的过程中,进行计数,看看现在已经是第几个数了,然后在直接进行输出!
如果当前是k个,说明已经遍历过那么多元素了,直接返回,如果不是,就继续往下遍历!
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int res = 0;
int count;
boolean preOrder(TreeNode root){
if(root == null) return false;;
if(preOrder(root.left)) return true;
count --;
if(count == 0) {
res = root.val;
return true;
}
return preOrder(root.right);
}
public int kthSmallest(TreeNode root, int k) {
count = k;
preOrder(root);
return res;
}
}
总结
- 通过招聘发现自己的是一个很重得失的人,别人有的,我也想要有!一样都不能缺!我得明白我想要什么吧!就是这一个好工作,然后好好挣钱,养家!
- 今天的题目做完了,二叉树,早上做了三道题,后续继续加油吧!