文章目录
104.二叉树的最大深度
我们将二叉树的问题细分为子问题
如果root为空节点,返回0
如果root 为单节点,返回1
如果root有左右孩子,那么返回左右节点的最大深度并 + 1
那么我们递归的思路有了,就是不断往下找,然后将左右孩子的两个深度的最大值返回,并且继续往上不断加不断判断最大值。
public int maxDepth(TreeNode root) {
if(root == null){
return 0;
}
int height = 1;
height += Math.max(maxDepth(root.left), maxDepth(root.right));
return height;
}
1026. 节点与其祖先之间的最大差值
这个最大差值怎么找,难道我们要一个一个加减然后判断吗,不。
注意,V = |A.val - B.val|,那么我们只需要列举孙子节点的最大值和最小值即可然后判断最大值即可(绝对值的意义实际是数与数的距离)
public int maxAncestorDiff(TreeNode root) {
return getdiff(root, root.val, root.val);
}
public int getdiff(TreeNode root, int mx, int mi){
if(root == null){
return 0;
}
int ans = 0;
ans = Math.max(Math.abs(root.val - mi), Math.abs(root.val - mx));
mx = Math.max(mx, root.val);
mi = Math.min(mi, root.val);
//更新最大值最小值
ans = Math.max(ans, Math.max(getdiff(root.left, mx, mi), getdiff(root.right, mx, mi)));
//在更新ans,把左子树和右子树遍历一遍
return ans;
}
701.二叉搜索树中的插入操作
只需要按照二叉搜索树的特性,见缝插针,就当在有序数组中插入一个值。
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root == null){
return new TreeNode(val, null, null);
}else if(root.val > val){
root.left = insertIntoBST(root.left, val);
}else if(root.val < val){
root.right = insertIntoBST(root.right, val);
}else {
return root;
}
return root;//非要一个返回值,不然报错,随便写的
}
669. 修剪二叉搜索树
这道题很会唬人,但我们只需要按部就班,先考虑如果
root.val > high
,那么我们就没有必要在root和他的右子树浪费时间,直接在左子树进行修剪,root.val < low
也同理。
然后我们走到了各个分支就继续当作子问题执行,从子问题入手,修剪左右子树,并返回左右子树修剪后的节点并连接即可。
public TreeNode trimBST(TreeNode root, int low, int high) {
if(root == null){
return null;
}
if(root.val > high){
return trimBST(root.left, low, high);
}//如果中间比右边界大,就往左走
if(root.val < low){
return trimBST(root.right, low, high);
}//同理
root.left = trimBST(root.left, low, high);
root.right = trimBST(root.right, low, high);
return root;
}
450. 删除二叉搜索树中的节点
先寻找再连接
public TreeNode deleteNode(TreeNode root, int key) {
if(root == null){
return null;
}else if(root.val > key){
root.left = deleteNode(root.left, key);
}else if(root.val < key){
root.right = deleteNode(root.right, key);
}else if (root.val == key){
if(root.left == null && root.right ==null){
return null;//让返回的节点为空,那么原来连接这个叶子结点的父节点就连了个空,即删除该叶子节点
}else if(root.left == null){
return root.right;
//如果为右斜树,就让右子树连接上父节点,这样删除也不影响顺序
}else if(root.right == null){
return root.left;
//同理
}
//走到这一步说明有左右子树
TreeNode p = root.right;
while(p.left != null){
p = p.left;
}
root.right = deleteNode(root.right, p.val);//断开p节点的连接,但保留p节点
p.left = root.left;
p.right = root.right;
//让p接替root,并且返回p,因为返回的是要连接的节点
return p;
}
return root;
}
230. 二叉搜索树中第K小的元素
这是一个二叉搜索树,我们可以中序遍历,来找到这个数
也可以通过一种特殊的办法,去统计结点的根节点左子树的节点个数来分情况讨论。
详见代码注释
public int kthSmallest(TreeNode root, int k) {
int leftnumber = getLeftNum(root.left);//获取左节点的次序
if(k == leftnumber + 1){//说明根节点就是我们要找的点
return root.val;
}else if(k <= leftnumber){
return kthSmallest(root.left, k); //继续往左找
}else{
return kthSmallest(root.right, k - leftnumber - 1);//往右找
//说明它在右边,那我们的k需要减去root的LeftNum,才能与右子树的LeftNum相匹配(右子树是root的右子树,而k是从最左边来计数的)
}
}
/**获取LeftNum的方法,其实也是获取到了根节点的左节点的位序*/
public int getLeftNum(TreeNode root){
if(root == null){
return 0;
}
int ans = 1;
ans += getLeftNum(root.left);
ans += getLeftNum(root.right);
//注意,左右都要加
return ans;
}
111.二叉树的最小深度
这道题和最大深度的递归代码相似,把max改成min,并且加判断就可以
但是我更想要用层序遍历来做,毕竟一如递归深似海,从此offer是路人
终止条件就是找到一个既无左子树也无右子树的节点就返回结果
public int minDepth(TreeNode root) {
if (root == null){
return 0;
}
int height = 1;
Deque<TreeNode> que = new LinkedList<>();
que.offer(root);
while(!que.isEmpty()){
int size = que.size();
while(size-- > 0){
TreeNode p = que.poll();
if(p.left == null && p.right == null){
return height;
}
if(p.left != null){
que.offer(p.left);
}
if(p.right != null){
que.offer(p.right);
}
}
++height;
}
return height;
}
235. 二叉搜索树的最近公共祖先
我们知道这是一个二叉搜索树,我们可以用二叉搜索树的性质,去获取一个路径,这个路径记录了根节点到节点p,节点q的所有途径节点,最近公共祖先,也就是结点p,结点q的路径的共同路径的最后一个节点,也就是分岔点。有了这个思路,加上二叉搜索树的特殊性质,我们可以很容易写出答案。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
List<TreeNode> p_path = getPath(root, p);
List<TreeNode> q_path = getPath(root, q);
TreeNode res = root;
for (int i = 0; i < Math.min(p_path.size(), q_path.size()); ++i) {
if(p_path.get(i) == q_path.get(i)){
res = p_path.get(i);
//找路径相同的最后一个点
}else{
break;
}
}
return res;
}
public List<TreeNode> getPath(TreeNode root, TreeNode key){
List<TreeNode> path = new LinkedList<>();
while(root != key){
path.add(root);
if (root.val > key.val){
root = root.left;
}else{
root = root.right;
}
}
return path;
}
654. 最大二叉树
题目中告诉我们递归的构造这个树,我们需要的是某个区间内的最大值的下标,我们在构造树的节点时,势必要引入两个参数,即当前的树及其子树的在数组内的区间(l, r),不断地二分去构造。
- 我们首先写出最简单的区间寻找最大值下标的函数,用来确定构造的父节点。
public int getMaxIndex(int[] nums, int l, int r){
int idx = l;
for (int i = l + 1; i <= r; ++i) {
if (nums[i] > nums[idx]){
idx = i;
}
}
return idx;
}
- 我们能确定构造的父节点之后还远远不够,我们还需要知道子节点,那么子节点大部分情况也是别的点的父节点,这样自上到下,一个一个迭代是很难实现的,需要递归实现。
public TreeNode constructMaximumBinaryTree(int[] nums) {
return MaxTree(nums, 0, nums.length - 1);
}
public TreeNode MaxTree(int[] nums, int l, int r){
if (l > r){
return null;
}
if (l == r){
return new TreeNode(nums[l], null, null);
}
int maxIndex = getMaxIndex(nums, l, r);
TreeNode root = new TreeNode(nums[maxIndex]);
root.left = MaxTree(nums, l, maxIndex - 1);
root.right = MaxTree(nums, maxIndex + 1, r);
return root;
}
public int getMaxIndex(int[] nums, int l, int r){
int idx = l;
for (int i = l + 1; i <= r; ++i) {
if (nums[i] > nums[idx]){
idx = i;
}
}
return idx;
}