例题1:二叉搜索树中的搜索
给定二叉搜索树(BST)的根节点 root
和一个整数值 val
。
你需要在 BST 中找到节点值等于 val
的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null
。
这道题乍一看是简单的递归题,但在单层处理逻辑中,我习惯地写
searchBST(root->left, val),
却忘了递归函数还有返回值。
递归函数的返回值是什么? 是 左子树如果搜索到了val,要将该节点返回。 如果不用一个变量将其接住,那么返回值不就没了
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if(root ==NULL) return NULL;
if(root->val == val) return root;
// else if(root->val < val) searchBST(root->right,val);
// else searchBST(root->left,val);
// return NULL; 递归函数的返回值是子树如果搜索到了val,要将该节点返回。 如果不用一个变量将其接住,那么返回值不就没了
TreeNode *result = NULL;
if(root->val < val) result = searchBST(root->right,val);
else if (root->val > val) result = searchBST(root->left,val);
return result;
}
};
例题2:验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
我一开始写的代码是不完全正确的,如下:
-
陷阱1
class Solution {
public boolean isValidBST(TreeNode root) {
//终止条件
//判断是否为二叉搜索树
if(root.left!=null && root.right!=null)
{
if(root.left.val >= root.val || root.right.val <= root.val)
return false;
return isValidBST(root.left)&&isValidBST(root.right);
}else if(root.left!=null)
{
if(root.left.val >= root.val) return false;
return isValidBST(root.left);
}else if(root.right!=null)
{
if(root.right.val <= root.val) return false;
return isValidBST(root.right);
}else
return true;
}
}
因为我单纯比较当前结点和左右子结点的大小,但是 我们要比较的是 左子树所有节点小于中间节点,右子树所有节点大于中间节点。所以以上代码的判断逻辑是错误的。
正确写法是,我们利用搜索二叉树的特性(左子树的所有结点都比根结点小,右子树的所有结点都比根结点大),将二叉树进行中序遍历,得到一个数组,我们判断其是否有序;
或者也可以节省这个数组,使用一个最小值进行中序遍历,判断下一个结点是否比当前结点的值大。
陷阱2
样例中最小节点 可能是int的最小值,如果这样使用最小的int来比较也是不行的。
此时可以初始化比较元素为longlong的最小值。
class Solution {
public long maxval = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if(root == null) return true;
boolean left = isValidBST(root.left); // 左
// 中序遍历,验证遍历的元素是不是从小到大
if (maxval < root.val) maxval = root.val; // 中
else return false;
boolean right = isValidBST(root.right); // 右
return left && right;
}
}
问题可以进一步演进:如果样例中根节点的val 可能是longlong的最小值 又要怎么办呢?
建议避免 初始化最小值,如下方法取到最左面节点的数值来比较。
class Solution {
public:
TreeNode* pre = NULL; // 用来记录前一个节点
bool isValidBST(TreeNode* root) {
if (root == NULL) return true;
bool left = isValidBST(root->left);
if (pre != NULL && pre->val >= root->val) return false;
pre = root; // 记录前一个节点
bool right = isValidBST(root->right);
return left && right;
}
};
例题3:二叉搜索树的最小绝对差
给你一个二叉搜索树的根节点 root
,返回 树中任意两不同节点值之间的最小差值 。
差值是一个正数,其数值等于两值之差的绝对值。
跟上一题一样,可以通过中序遍历得到所有树结点,然后遍历一遍数组得到最小差值。
class Solution {
public:
vector<int> result;
void traversal(TreeNode* root){
if(root == NULL) return ;
traversal(root -> left);
result.push_back(root -> val);
traversal(root -> right);
}
int getMinimumDifference(TreeNode* root) {
traversal(root);
if (result.size() < 2) return 0;
int num = INT_MAX;
for (int i = 1; i < result.size(); i++) { // 统计有序数组的最小差值
num = min(num, result[i] - result[i-1]);
}
return num;
}
};
例题4:把二叉搜索树转换为累加树
给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node
的新值等于原树中大于或等于 node.val
的值之和。
提醒一下,二叉搜索树满足下列约束条件:
- 节点的左子树仅包含键 小于 节点键的节点。
- 节点的右子树仅包含键 大于 节点键的节点。
- 左右子树也必须是二叉搜索树。
如果使用数组,我们将搜索二叉树进行中序遍历,然后累加,即:数组最后1个值更新为自身,倒数第2个值为自身和最后1个值的累加....
换算到搜索二叉树,就是从右往左依次累加,也就是 右 -> 中 -> 左 的顺序。
1、递归函数返回类型及参数
根据上述的分析,遍历树不需要传什么参,传一个树结点指针就行。同时还要个全局变量来存储之前的累加值。
2、终止条件
遇空就终止。
3、单层逻辑
traversal(root -> right);
root -> val += total;
total = root -> val;
traversal(root -> left);
整体代码:
class Solution {
public:
int total = 0;
void traversal(TreeNode* root){
if(root == NULL) return;
traversal(root -> right);
root -> val += total;
total = root -> val;
traversal(root -> left);
}
TreeNode* convertBST(TreeNode* root) {
traversal(root);
return root;
}
};