654.最大二叉树
题目链接:654. 最大二叉树 - 力扣(LeetCode)
给定一个不重复的整数数组 nums
。返回 nums
构建的 最大二叉树 。
解题思路:
首先,找到数组中的最大值,这个值将会是二叉树的根节点。然后,使用最大值左边的数组来递归构建左子树,使用最大值右边的数组来递归构建右子树。
class Solution {
public:
// 获取最大值的索引
int getMaxIndex(const vector<int>& nums) {
int maxIndex = 0;
for (int i = 1; i < nums.size(); ++i) {
if (nums[i] > nums[maxIndex]) {
maxIndex = i;
}
}
return maxIndex;
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
if (nums.size() == 0)
return nullptr;
// 获取最大值的索引
int maxIndex = getMaxIndex(nums);
TreeNode* root = new TreeNode(nums[maxIndex]);
// 分割左右子树
vector<int> leftNodes(nums.begin(), nums.begin() + maxIndex);
vector<int> rightNodes(nums.begin() + maxIndex + 1, nums.end());
// 递归构建左右子树
root->left = constructMaximumBinaryTree(leftNodes);
root->right = constructMaximumBinaryTree(rightNodes);
return root;
}
};
以上代码效率不高,每次还要切割的时候每次都要定义新的vector(也就是数组)。优化思路,就是每次分隔不用定义新的数组,而是通过下标索引直接在原数组上操作。
优化后的代码:
class Solution {
public:
// 获取最大值的索引
int getMaxIndex(const vector<int>& nums) {
int maxIndex = 0;
for (int i = 1; i < nums.size(); ++i) {
if (nums[i] > nums[maxIndex]) {
maxIndex = i;
}
}
return maxIndex;
}
// 构建最大二叉树
TreeNode* constructTree(vector<int>& nums, int start, int end) {
if (start > end)
return nullptr;
// 获取最大值的索引
int maxIndex = getMaxIndex(
vector<int>(nums.begin() + start, nums.begin() + end + 1));
TreeNode* root = new TreeNode(nums[start + maxIndex]);
// 递归构建左右子树
root->left =
constructTree(nums, start, start + maxIndex - 1);
root->right =
constructTree(nums, start + maxIndex + 1, end);
return root;
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
return constructTree(nums,0,nums.size()-1);
}
};
注意类似用数组构造二叉树的题目,每次分隔尽量不要定义新的数组,而是通过下标索引直接在原数组上操作,这样可以节约时间和空间上的开销。
617.合并二叉树
题目链接:617. 合并二叉树 - 力扣(LeetCode)
给你两棵二叉树: root1
和 root2
。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。返回合并后的二叉树。
解题思路: 递归遍历两棵二叉树
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. 二叉搜索树中的搜索 - 力扣(LeetCode)
给定二叉搜索树(BST)的根节点 root
和一个整数值 val
。你需要在 BST 中找到节点值等于 val
的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null
。
解题思路:
利用二叉树的性质,使用迭代法比较简单。借此题练个手,练习递归法。
递归法
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;
}
};
迭代法
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
while (root != NULL) {
if (root->val > val) root = root->left;
else if (root->val < val) root = root->right;
else return root;
}
return NULL;
}
};
98.验证二叉搜索树
题目链接:98. 验证二叉搜索树 - 力扣(LeetCode)
给你一个二叉树的根节点 root
,判断其是否是一个有效的二叉搜索树。
解题思路:
要知道中序遍历下,输出的二叉搜索树节点的数值是有序序列。
有了这个特性,验证二叉搜索树,就相当于变成了判断一个序列是不是递增的了。
易错误区:
不能单纯的比较左节点小于中间节点,右节点大于中间节点就完事了。我们要比较的是 左子树所有节点小于中间节点,右子树所有节点大于中间节点。
节点10大于左节点5,小于右节点15,但右子树里出现了一个6 这就不符合了!
class Solution {
private:
vector<int> vec;
void traversal(TreeNode* root) {
if (root == NULL) return;
traversal(root->left);
vec.push_back(root->val); // 将二叉搜索树转换为有序数组
traversal(root->right);
}
public:
bool isValidBST(TreeNode* root) {
vec.clear(); // 不加这句在leetcode上也可以过,但最好加上
traversal(root);
for (int i = 1; i < vec.size(); i++) {
// 注意要小于等于,搜索树里不能有相同元素
if (vec[i] <= vec[i - 1]) return false;
}
return true;
}
};
以上代码中,我们把二叉树转变为数组来判断,是最直观的,但其实不用转变成数组,可以在递归遍历的过程中直接判断是否有序。
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;
}
};