654. 最大二叉树:力扣
617. 合并二叉树:力扣
700. 二叉搜索树中的搜索:力扣
98. 验证二叉搜索树:力扣
什么时候递归函数前面加if,什么时候不加if?
一般情况来说:如果让空节点(空指针)进入递归,就不加if,如果不让空节点进入递归,就加if限制一下, 终止条件也会相应的调整。
654. 最大二叉树
给定一个不重复的整数数组 nums
。 最大二叉树 可以用下面的算法从 nums
递归地构建:
- 创建一个根节点,其值为
nums
中的最大值。 - 递归地在最大值 左边 的 子数组前缀上 构建左子树。
- 递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums
构建的 最大二叉树 。
class Solution {
public:
TreeNode* getTree(vector<int>::iterator begin, vector<int>::iterator end){
if(begin == end) return nullptr;
int top = *begin, pos = 0;
for(int i = 0; begin + i != end; i++){
if(*(begin + i) > top){
pos = i;
top = *(begin + i);
}
}
TreeNode* root = new TreeNode(top);
root->left = getTree(begin, begin + pos);
root->right = getTree(begin + pos + 1, end);
return root;
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
TreeNode* root = getTree(nums.begin(), nums.end());
return root;
}
};
617. 合并二叉树
给你两棵二叉树: root1
和 root2
。当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。返回合并后的二叉树。
递归解法:
class Solution {
public:
TreeNode* getTree(TreeNode* root1, TreeNode* root2){
if(root1 != nullptr && root2 != nullptr){
root1->val += root2->val;
root1->left = getTree(root1->left, root2->left);
root1->right = getTree(root1->right, root2->right);
return root1;
}
else if(root1 != nullptr) return root1;
else if(root2 != nullptr) return root2;
else return nullptr;
}
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
TreeNode* root = getTree(root1, root2);
return root;
}
};
迭代解法时,注意都把孩子节点拼接在root1上,即可实现合并。
700. 二叉搜索树中的搜索
给定二叉搜索树(BST)的根节点 root
和一个整数值 val
。你需要在 BST 中找到节点值等于 val
的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null
。
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
TreeNode* cur = root;
while(cur != nullptr && cur->val != val){
if(val > cur->val){
cur = cur->right;
}
else{
cur = cur->left;
}
}
return cur;
}
};
98. 验证二叉搜索树
给你一个二叉树的根节点 root
,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
- 节点的左子树只包含 小于 当前节点的数。
- 节点的右子树只包含 大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
解法:
利用二叉搜索树的性质,二叉搜索树的中序遍历有序,因此中序遍历二叉搜索树,验证是否有序
如果用int记录遍历到的值,需要初始化一个最小值,但是值可以取到int的最小值,会有bad case: [-2147483648,-2147483648]。因此值需要用longlong来处理,不能用int。
为了确保正确,或值域取到long long的最小值,可以用指针记录前一个遍历到的节点,初始化为nullptr,不记录初始值。
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;
}
};
递归解法:
class Solution {
public:
bool inorder(TreeNode* root, long long& pre){
if(root == nullptr) return true;
bool temp = false;
// 先遍历左子树
if(root->left != nullptr){
temp = inorder(root->left, pre);
if(!temp) return false;
}
// 当前节点
if(root->val <= pre) return false;
pre = root->val;
// 右子树
if(root->right != nullptr){
temp = inorder(root->right, pre);
if(!temp) return false;
}
return true;
}
bool isValidBST(TreeNode* root) {
if(root == nullptr) return true;
long long pre = LLONG_MIN;
return inorder(root, pre);
}
};
迭代解法:
利用栈进行中序遍历。
入栈时,循环把自己的左孩子节点加入栈,保证对于每个节点,左孩子节点先于自己访问。
出栈时,先访问自身,再将自身的右孩子加入栈,右孩子入栈时循环将其左孩子节点入栈,保证对于每个
class Solution {
public:
bool inorder(TreeNode* root){
TreeNode* n = root;
stack<TreeNode*> st;
if(root != nullptr) st.push(root);
while(n->left != nullptr){ // inorder
st.push(n->left);
n = n->left;
}
long long cur = (long long)INT_MIN - 1;
while(!st.empty()){
n = st.top();
if((long long)(n->val) <= cur) return false;
cur = n->val;
st.pop();
// visit father
while(!st.empty() && n->right == nullptr){
n = st.top();
if((long long)(n->val) <= cur) return false;
cur = n->val;
st.pop();
}
// visit right
if(n->right != nullptr){
st.push(n->right);
n = n->right;
while(n->left != nullptr){ // inorder
st.push(n->left);
n = n->left;
}
}
}
return true;
}
bool isValidBST(TreeNode* root) {
if(root == nullptr) return true;
return inorder(root);
}
};