654.最大二叉树
思路:
构造二叉树一般都是使用前序遍历,先构造出二叉树的根节点然后递归构造我们的左右子树。
递归三部曲:
1.确定返回值和参数:因为构造二叉树所以要返回根节点,参数是数组。
TreeNode* constructMaximumBinaryTree(vector<int>& nums)
2.确定递归终止条件:如果nums数组的大小为1,说明遍历到了叶子节点,那么return 根据数组元素构造出来的节点
TreeNode* node = new TreeNode(0);
if (nums.size() == 1) {
node->val = nums[0];
return node;
}
3.单层循环逻辑:遍历数组寻找最大值和最大值所在的位置,找到最大值对当前根节点进行赋值,找到最大值的位置用于切割数组的左右两个数组。找到切割点后进行切割数组,然后向左右进行递归构造,向左右进行递归构造的条件是,左右数组的大小至少大于1.
int maxValue = 0;
int maxValueIndex = 0;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] > maxValue) {
maxValue = nums[i];
maxValueIndex = i;
}
}
TreeNode* node = new TreeNode(0);
node->val = maxValue;
if (maxValueIndex > 0) {
vector<int> newVec(nums.begin(), nums.begin() + maxValueIndex);
node->left = constructMaximumBinaryTree(newVec);
}
if (maxValueIndex < (nums.size() - 1)) {
vector<int> newVec(nums.begin() + maxValueIndex + 1, nums.end());
node->right = constructMaximumBinaryTree(newVec);
}
整体代码:
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 (maxValueIndex > 0) {
vector<int> newVec(nums.begin(), nums.begin() + maxValueIndex);
node->left = constructMaximumBinaryTree(newVec);
}
// 最大值所在的下标右区间 构造右子树
if (maxValueIndex < (nums.size() - 1)) {
vector<int> newVec(nums.begin() + maxValueIndex + 1, nums.end());
node->right = constructMaximumBinaryTree(newVec);
}
return node;
}
};
这样写出的代码还比较冗余,每次递归时都需要切割原数组创建新的vector,所以为了优化代码,我们可以不创建新数组而是通过操作索引小标来分割数组。
class Solution {
private:
// 在左闭右开区间[left, right),构造二叉树
TreeNode* traversal(vector<int>& nums, int left, int right) {
if (left >= right) return nullptr;
// 分割点下标:maxValueIndex
int maxValueIndex = left;
for (int i = left + 1; i < right; ++i) {
if (nums[i] > nums[maxValueIndex]) maxValueIndex = i;
}
TreeNode* root = new TreeNode(nums[maxValueIndex]);
// 左闭右开:[left, maxValueIndex)
root->left = traversal(nums, left, maxValueIndex);
// 左闭右开:[maxValueIndex + 1, right)
root->right = traversal(nums, maxValueIndex + 1, right);
return root;
}
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
return traversal(nums, 0, nums.size());
}
};
可以发现上面的代码看上去简洁一些,主要是因为第二版其实是允许空节点进入递归,所以不用在递归的时候加判断节点是否为空
617.合并二叉树
思路:
同时操作两颗二叉树的题目,涉及到同时遍历二叉树相同位置的节点。
递归三部曲:
1.确定返回值类型和参数:返回值类型返回合并后的二叉树的根节点,所以返回值类型是指向二叉树根节点的指针,参数是操作的两颗二叉树根节点
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
2.确定递归终止条件:如果遍历的二叉树t1的节点为null,return t2遍历到的节点,如果遍历到t2的节点为null则返回t1,可能这时会出现一个疑惑t1和t2同时为null怎么办?其实如果t1和t2同时为null返回t1和t2等同于返回null.
if (t1 == NULL) return t2; // 如果t1为空,合并之后就应该是t2
if (t2 == NULL) return t1; // 如果t2为空,合并之后就应该是t1
3.确定单层递归:
t1->val += t2->val;
代码展示:
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;
}
};
注意!!!!!有一个点大家可能会疑惑,比如二叉树1只有一个结点,二叉树2有很多层结点,递归循环只进行了一层,表面看二叉树1只接上了左右两个子节点,但是左右两个子节点会将之前的左右子树都带上,而且不用担心的是,原先的二叉树1,对应结点的地方都为空,所以直接继承了二多的节点,而不会出错。
700.二叉搜索树中的搜索
思路:
这与普通二叉树不同,这是一颗二叉搜索树。
二叉搜索树的概念:根节点的左子树里的节点的值都小于根节点的值,根节点的右子树里的所有节点的值都大于根节点,左右子树也满足这一特性。
利用二叉搜索树的特性,我们可以知道,遍历这颗二叉树找元素不需要使用前中后序遍历,我们可以有方向的遍历一颗二叉树。
递归三部曲:
1.确定返回值类型和参数:因为要返回找到的根节点所以需要返回指向二叉树结点的指针,参数是二叉树根节点和目标值。
2.确定终止条件:
if (root == NULL || root->val == val) return root;
3.确定单层逻辑:如果val>root->val,向右递归,如果val<root->val,向左递归。
TreeNode* result = NULL;
if (root->val > val) result = searchBST(root->left, val);
if (root->val < val) result = searchBST(root->right, val);
return result;
代码展示:
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;
}
};
这里不用在左右递归后面写如果result!=null,就return之类的话,因为这是二叉搜索树,是有方向的递归搜索,如果找到了,返回回去,也是按照原路直上。
98.验证二叉搜索树
思路:
验证一颗二叉树是不是二叉搜索树,实质上就是验证他的结点顺序是不是正确的,根据二叉搜索树的特性,如果我们使用中序遍历二叉树搜索树,那么得到的结果是有序的。
方法1:我们遍历中序遍历一颗二叉搜索树,将遍历到的节点压入数组中,然后再验证数组是否是有序数组,如果有序,那么就是二叉搜索树
方法2:不创建数组,直接在二叉树中验证是否是二叉搜索树,定义一个全局变量maxvalue,初始值赋值为LONG_INT,用中序遍历,如果如果遍历到的数值比maxvalue大,就将maxvalue赋值为该结点的值,如果不比他大,那么就不是有序的,不是二叉搜索树。return false;
方法2的弊端:二叉树左下角的数值也为LONG_MIN那么无法使用这个方法。
方法3:定义一个pre全局指针用来记录遍历到上一个节点的指针,如果每次遍历到的节点的值都大于上一次遍历到节点的值,证明是二叉搜索树。
方法3代码细节:
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;
}
};