617.合并二叉树
文章讲解:合并二叉树
思路以及遍历顺序
主要是考察一起操作两个二叉树的能力。
还是使用前序是最合理的,先合并根结点,再向左处理。比较符合逻辑。
伪代码
- 确定递归函数的参数和返回值:首先要合入两个二叉树,那么参数至少是要传入两个二叉树的根节点,返回值就是合并之后二叉树的根节点。
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2)
- 确定终止条件:因为是传入了两个树,那么就有两个树遍历的节点t1 和 t2,如果t1 == NULL 了,两个树合并就应该是 t2 了(如果t2也为NULL也无所谓,合并之后就是NULL)。反过来如果t2 == NULL,那么两个数合并就是t1(如果t1也为NULL也无所谓,合并之后就是NULL)。
if (t1 == NULL) return t2; // 如果t1为空,合并之后就应该是t2
if (t2 == NULL) return t1; // 如果t2为空,合并之后就应该是t1
- 确定单层递归的逻辑:
单层递归的逻辑就比较好写了,这里我们重复利用一下t1这个树,t1就是合并之后树的根节点(就是修改了原来树的结构)。
那么单层递归中,就要把两棵树的元素加到一起。
t1->val += t2->val; //中
接下来t1 的左子树是:合并 t1左子树 t2左子树之后的左子树。
t1 的右子树:是 合并 t1右子树 t2右子树之后的右子树。
最终t1就是合并之后的根节点。
t1->left = mergeTrees(t1->left, t2->left); //左
t1->right = mergeTrees(t1->right, t2->right); //右
return t1;
CPP代码
//前序遍历
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
if (t1 == NULL) return t2; // 如果t1为空,合并之后就应该是t2
if (t2 == NULL) return t1; // 如果t2为空,合并之后就应该是t1
// 修改了t1的数值和结构
t1->val += t2->val; // 中
t1->left = mergeTrees(t1->left, t2->left); // 左
t1->right = mergeTrees(t1->right, t2->right); // 右
return t1;
}
};
对于中序、后序遍历改变顺序即可。
700.二叉搜索树中的搜索
文章讲解:700.二叉搜索树中的搜索
搜索树的特性
二叉搜索树中,根结点比左子树结点都要大,比右子树结点都要小。同样,其左右子树也符合这个规则。
在二叉搜索树中,它自带顺序,所以也不关注其前中后序了。因为其结点的大小规则,已经为我们确定了搜索规则。
伪代码
- 确定递归函数的参数和返回值:递归函数的参数传入的就是根节点和要搜索的数值,返回的就是以这个搜索数值所在的节点。
TreeNode* searchBST(TreeNode* root, int val)
- 确定终止条件:如果root为空,或者找到这个数值了,就返回root节点。
if (root == NULL || root->val == val) return root;
- 确定单层递归逻辑:因为二叉搜索树的结点是有序的,所以可以有方向得去搜索。
如果root->val > val,搜索左子树,如果root->root < val,就搜索右子树,如果最后都没有搜索到,就返回NULL
TreeNode* result = NULL;
if (root->val > val) result = searchBST(root->left, val);
if (root->val < val) result = searchBST(root->right, val);
return result;
CPP代码
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if (root == NULL || root->val == val) return root;
TreeNode* result = NULL;
if (root->val > val) result = searchBST(root->left, val);
if (root->val < val) result = searchBST(root->right, val);
return result;
}
};
//or
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if (root == NULL || root->val == val) return root;
if (root->val > val) return searchBST(root->left, val);
if (root->val < val) return searchBST(root->right, val);
return NULL;
}
};
98.验证二叉搜索树
文章讲解:98.验证二叉搜索树
其实本题的精髓就是抓住:二叉搜索树的中序遍历结果是严格从小到大的!
搜索树的特性
左子树里的结点元素都小于中结点,右子树的所有元素都大于中结点;同时左右子树均符合这样的规则。
所以根据此特性,如果我们中序遍历,那么这个元素就是有序的。
直白想法
直接中序遍历二叉树,然后把所有元素都保存到一个数组上,然后判断这个数组是不是单调递增的就可以了。
- 函数的返回值和参数、确定终止条件、单层递归逻辑
bool isvalid(TreeNode* root){
if (root == NULL) return true;//啥二叉树都是空结点
isvalid(root->left); //中序遍历
vec.push_back(root->val);
isvalid(root->right);
}
然而!该方法并不是最有办法,因为我们额外定义了一个数组,然后把二叉树变成一个数组,然后判断数组是否有序。太麻烦了,我们可以直接在遍历过程中判断这个元素是不是单调递增的。
代码误区
经典错误:
if (root->val > root->left->val && root->val < root->right->val)
return
上述代码逻辑就是,root的值大于左孩子的值并且root的值大于右孩子的值。
但这样就进入了一个误区,二叉搜索树是要求根结点比左子树的所有结点都要大,不仅仅是左孩子。
伪代码
先定义一个全局变量:用来比较遍历的节点是否有序,因为后台测试数据中有int最小值,所以定义为longlong的类型,初始化为longlong最小值。
long long maxVal = LONG_MIN; // 因为后台测试数据中有int最小值
-
函数的返回值和参数:注意递归函数要有bool类型的返回值, 在二叉树:递归函数究竟什么时候需要返回值,什么时候不要返回值?中讲了,只有寻找某一条边(或者一个节点)的时候,递归函数会有bool类型的返回值。
其实本题是同样的道理,我们在寻找一个不符合条件的节点,如果没有找到这个节点就遍历了整个树,如果找到不符合的节点了,立刻返回。
bool isValidBST(TreeNode* root)
- 确定终止条件:空结点也是二叉搜索树
if (root == NULL) return true;
- 确定单层递归逻辑:中序遍历,一直更新maxVal,一旦发现maxVal >= root->val,就返回false,注意元素相同时候也要返回false。
bool left = isValidBST(root->left); // 左
// 中序遍历,验证遍历的元素是不是从小到大
if (root->val > maxVal) maxVal = root->val; // 中
else return false;
bool right = isValidBST(root->right); // 右
return left && right;
CPP代码
class Solution {
public:
long long maxVal = LONG_MIN; // 因为后台测试数据中有int最小值
bool isValidBST(TreeNode* root) {
if (root == NULL) return true;
bool left = isValidBST(root->left);
// 中序遍历,验证遍历的元素是不是从小到大
if (maxVal < root->val) maxVal = root->val;
else return false;
bool right = isValidBST(root->right);
return left && right;
}
};
双指针优化
我们上面借助了一个中间值,其实还是不好,因为有可能搜索树的最小值比中间值还要小,那就不行了。
所以我们用双指针直接进行比较!
TreeNode* pre = NULL; //记录我们当前遍历的前一个结点,然后我们的root和pre进行比较
//只用重写中间逻辑
if (pre != NULL && pre->val >= root->val)
return false;
pre = root;
//从这个地方才开始,pre实现了记录root的前一个结点,在此之前pre总是为NULL