BSTII
继续上上次的二叉树。上次看到二叉搜索树,看了一道BST转换累加树的题。
二叉树转累加树
每个节点的值要更新成为比他大的节点的值的加和。相当于计算当前节点右子树的和,首先按照特性中序遍历BST就能得到升序排序,那反过来先遍历右子树不就得到降序了吗。类似的,先遍历右子树,然后累加和,加起来之后赋值给根节点就结束了。代码如下就几行。
class Solution {
public:
TreeNode* convertBST(TreeNode* root) {
traverse(root);
return root;
}
int sum=0;
void traverse(TreeNode *root){
if(root==NULL) return ;
traverse(root->right);
sum+=root->val;
root->val=sum;
traverse(root->left);
}
};
判断二叉搜索树的合法性。
一般想到的就是递归整棵树,左子大于根大于右子,实际并不然。这只考虑了左右孩子而没有考虑左右子树,实际的做法是,传参,左子树的根节点是最大的,右子树的根节点是最大的。这样递归即可
boolean isValidBST(TreeNode root) {
return isValidBST(root, null, null);
}
/* 限定以 root 为根的子树节点必须满足 max.val > root.val > min.val */
boolean isValidBST(TreeNode root, TreeNode min, TreeNode max) {
// base case
if (root == null) return true;
// 若 root.val 不符合 max 和 min 的限制,说明不是合法 BST
if (min != null && root.val <= min.val) return false;
if (max != null && root.val >= max.val) return false;
// 限定左子树的最大值是 root.val,右子树的最小值是 root.val
return isValidBST(root.left, min, root)
&& isValidBST(root.right, root, max);
}
接下来有一堆增删改查。其中引入一个模版。
void BST(TreeNode root, int target) {
if (root.val == target)
// 找到目标,做点什么
if (root.val < target)
BST(root.right, target);
if (root.val > target)
BST(root.left, target);
}
在BST中寻找一个数
类似二分查找的思想,如果这个值大于根节点,说明一定在右边,只需要遍历右子树,小于的话就是左边,等于正好,所以有:
boolean isValidBST(TreeNode root) {
return isValidBST(root, null, null);
}
/* 限定以 root 为根的子树节点必须满足 max.val > root.val > min.val */
boolean isValidBST(TreeNode root, TreeNode min, TreeNode max) {
// base case
if (root == null) return true;
// 若 root.val 不符合 max 和 min 的限制,说明不是合法 BST
if (min != null && root.val <= min.val) return false;
if (max != null && root.val >= max.val) return false;
// 限定左子树的最大值是 root.val,右子树的最小值是 root.val
return isValidBST(root.left, min, root)
&& isValidBST(root.right, root, max);
}
插入一个数
先查,找到插入位置,再插入,返回TreeNode类型就行
TreeNode insertIntoBST(TreeNode root, int val) {
// 找到空位置插入新节点
if (root == null) return new TreeNode(val);
// if (root.val == val)
// BST 中一般不会插入已存在元素
if (root.val < val)
root.right = insertIntoBST(root.right, val);
if (root.val > val)
root.left = insertIntoBST(root.left, val);
return root;
}
因为会正好在你找的那个节点的位置,左右叶子结点那里插进去。
BST删除一个数
最复杂的一种,因为需要查,再改,改还不容易,分三种情况。
- 要删的这个节点在末端,直接删
- 要删的节点有一个非空子节点,让孩子接替自己的位置
- 有两个子节点,为了不破坏BST,需要找到左子树的最大或者右子树的最小来接替自己。选择后者。
有如下代码:
TreeNode deleteNode(TreeNode root, int key) {
if (root == null) return null;
if (root.val == key) {
// 这两个 if 把情况 1 和 2 都正确处理了
if (root.left == null) return root.right;
if (root.right == null) return root.left;
// 处理情况 3
TreeNode minNode = getMin(root.right);
root.val = minNode.val;
root.right = deleteNode(root.right, minNode.val);
} else if (root.val > key) {
root.left = deleteNode(root.left, key);
} else if (root.val < key) {
root.right = deleteNode(root.right, key);
}
return root;
}
TreeNode getMin(TreeNode node) {
// BST 最左边的就是最小的
while (node.left != null) node = node.left;
return node;
}
比较难,需要理解。
几个题。
验证BST
做一遍才能有理解。看起来简单理解上还是需要加强。
- 如果当前节点会对下面的子节点有整体影响,可以通过辅助函数增长参数列表,借助参数传递信息。
这句话还是比较重要的。一定得学会自己建立辅助函数。
class Solution {
public:
bool isValidBST(TreeNode* root) {
return helper(root,NULL,NULL);
}
bool helper(TreeNode *root,TreeNode *min,TreeNode *max){
if(root==NULL) return true;//如果到该节点是空了,那就返回true,该子树都空了还能不是BST吗
if(min!=NULL&&root->val<=min->val) return false;//注意传参,首先最小节点不能是空而且当前节点的值如果小雨最小值的话就不是。啥意思呢,表达的意思就是如果你这个右子树的值比你的根节点的值还小,肯定不是BST。那就解释的通了,我传root传右子树,传min传根节点,如果根节点都空了要么就是一次遍历要么就是没意义。
if(max!=NULL&&root->val>=max->val) return false;//如果你这个//这里也一样了,右边子树肯定要比根节点大的,如果左子树的值比根节点还大,那肯定是不对的
return helper(root->left,min,root)&&helper(root->right,root,max);//按上述说法传参就完事了
}
};
查找BST子树
直接代码吧没啥好说的
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if(root==NULL) return NULL;
if(root->val==val) return root;
if(val>root->val) return searchBST(root->right,val);
if(val<root->val) return searchBST(root->left,val);
return root;
}
};
插入BST节点
和上面说的差不多,先找到再插入。
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root==NULL) return new TreeNode(val);//如果遍历到底了,叶子结点了,就返回一个新的节点的值
if(val>root->val) root->right=insertIntoBST(root->right,val);//如果这个值大于根节点,那么把它放在右子树。这个地方一般是递归到最后一层了直接加进去就好
if(val<root->val) root->left=insertIntoBST(root->left,val);//如果小于,放在左子树
return root;
}
};
删除BST的节点
相对问题最多的一道,但是按照上述思路走完就行。
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if(root==NULL) return NULL;
if(key==root->val){
//第一种情况是左右都没有,直接返回空,下面的两行就能解决这个问题了
if(root->right==NULL)return root->left;
if(root->left==NULL) return root->right;//以上对应第二种情况,要删除的节点只有左子树或者右子树,需要把他的剩下的另一个孩子直接拼上来。
// 3 2
// 2 null ->null null
//排除以上两种情况,就剩最麻烦的左右子都有了。这时候两种选择,要么选择左子树最大的替换他,要么选择右子树最小的。这边选择后者。
TreeNode *MinNode=getMin(root->right);//在右子树直接往左走到底就是最底下。设置一个新新节点等于该节点左子树的最小值节点
root->val=MinNode->val;//更新这个节点值
root->right=deleteNode(root->right,MinNode->val);
}//同时在右子树中删除该最小值点
else if(key>root->val) root->right=deleteNode(root->right,key);//注意这个地方一定要加else!因为已经更新了root->val的值,再进行比较的时候如果不else就会和小的进行比较,出现错误
else if(key<root->val) root->left=deleteNode(root->left,key);
return root;
}
TreeNode *getMin(TreeNode *root){
while(root->left!=NULL) root=root->left;//右子树,直接往左走到底
return root;
}
};