今天的题包括昨天的根据前中/后中序建立新树,都用到了TreeNode作为返回值,一定要学习并且接受、习惯这种返回值。
题目:654.最大二叉树(1.需要自己写代码重做 2.学习如何进一步优化代码)
构造二叉树类的题目都用前序遍历的题目
做递归时写不写if判断在于终止条件,保证其不会出现空指针异常或者访问越界的情况
思路:(直接用主函数)
终止条件:数组的长度为1,直接返回构造的节点
单层递归逻辑:
1.for循环找到数组中的最大值及其下标
2.分割左右数组(注意边界处理,左闭右开)
3.进行递归
4.建立节点并返回
优化:(自己写一个函数)每一层递归不重新构造数组,通过传入下标参数的改变值来控制,大大节省空间效率。
class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
TreeNode *node = new TreeNode(0);
if(nums.size() == 1) {
node->val = nums[0];
return node;
}
//找到数组中最大的值和下标
int maxValue = 0;
int maxValueIndex = 0;
for(int i = 0; i < nums.size(); i++){
if(nums[i] > maxValue){
maxValue = nums[i];
maxValueIndex = i;
}
}
node->val = maxValue;
//最大值所在的下标左区间 构造左子树
//if进行条件判断
if(maxValueIndex > 0){
vector<int> leftnums(nums.begin(), nums.begin() + maxValueIndex);
node->left = constructMaximumBinaryTree(leftnums); //对于Treenode* 这种返回值需要好好理解一下
}
if(maxValueIndex < nums.size() - 1){ //易错:这里if的判断应该是maxValueIndex < nums.size() - 1而不是maxValueIndex < nums.size()
vector<int> rightnums(nums.begin() + maxValueIndex + 1, nums.end());
node->right = constructMaximumBinaryTree(rightnums);
}
return node;
}
};
题目:617.合并二叉树
这道题没见过,同时操作两个二叉树。
终止条件:学习同时处理两个树时的终止条件
没有建立新节点,直接在root1的原地修改
终止条件:
如果root1 == NULL,则root2剩下的节点直接并入构造的新树
如果root2 == NULL,则root1剩下的节点直接并入构造的新树
单层递归逻辑:
接着修改root1的数值和结构
也可以new一个新root,这样看起来会更直观一些
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if(root1 == NULL) return root2;
if(root2 == NULL) return root1;
root1->val += root2->val; //中
root1->left = mergeTrees(root1->left, root2->left); //左
root1->right = mergeTrees(root1->right, root2->right); //右
return root1;
}
};
题目:700.二叉搜索树
自己写出来的!!!!!牛牛牛牛!!!!
用时还很短!!!
二叉搜索树有何特性?
根节点比左子树里所有数值都大,根节点比右子树里所有数值都小。该节点的左子树和右子树都满足这个特点。
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
//终止条件:遍历到叶子节点,如果叶子节点不满足条件,返回NULL
if(root->left == NULL && root->right == NULL && root->val != val) return NULL;
//不管其是否是叶子节点:只要等于val,返回这个节点(这一步也算在终止条件里)
if(root->val == val) return root; //中
//单层递归逻辑
TreeNode *node;
if(root->left){
node = searchBST(root->left, val); //左
if(node != NULL) return node;
}
if(root->right){
node = searchBST(root->right, val); //右
if(node != NULL) return node;
}
return NULL;
}
};
但是因为二叉搜索树的特性,遍历顺序与一般的树不同,用中序遍历会大大加快搜索效率。因为有特性,迭代法也非常简单
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;
}
};
题目:98.验证二叉搜索树
如果是一颗二叉搜索树,利用左中右,就可以将整棵树从小到大依次遍历。
故本题的直白想法就是:通过中序遍历构造一个数列,判断这个数列是否有序
代码略。
容易有的误区:根节点是比左子树的所有的value都大,比右子树所有的value都小,而不是仅仅比左孩子的value大,比右孩子的value小
优化版本:直接在树里面,通过中序遍历(左中右)判断是否是搜索二叉树,如果是一颗搜索二叉树,通过中序遍历到的节点一定是不断递增的。
代码如下:
class Solution {
public:
long long maxValue = LONG_MIN; //设置maxValue的作用:记录前一个遍历节点的数字,与本次递归遍历的节点作比较,判断是否满足二叉搜索树的条件
bool isValidBST(TreeNode* root) {
if(root == NULL) return true;
bool left = isValidBST(root->left); //左
if(maxValue < root->val){ //中
maxValue = root->val;
}else{
return false;
}
bool right = isValidBST(root->right); //右
return left && right;
}
};
优化:利用双指针,直接比较前一个节点和后一个节点
关键:如何用指针记录前一个节点
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){ //判断条件中一定要加上pre != NULL,第一个节点不需要进行判断,否则会空指针异常
return false; //中
}else{
pre = root;
}
bool right = isValidBST(root->right); //右
return left && right;
}
};