654.最大二叉树
每次都从序列中搜索最大值,以该值为界分割序列进行递归即可。
· 返回值类型:TreeNode*,返回当前子树的节点指针
· 传入参数:
vector<int> nums:用于构建当前子树的序列数组
· 终止条件:序列为空,返回nullptr表示当前子树为空
· 单层递归逻辑——前序遍历:
中:从序列中搜索最大值,构造根节点指针
左:递归构造左子树,传入最大值左侧的序列
右:递归构造右子树,传入最大值右侧的序列
TreeNode* traversal(vector<int> nums) {
if (nums.empty())
return nullptr;
// 中,获取序列中的最大值,以此构建子树根节点
// 剪枝,如果数组中只有一个元素则直接返回
if (nums.size() == 1)
return new TreeNode(nums[0]);
auto maxIter = std::max_element(nums.begin(), nums.end()); // 使用了max_element库函数
TreeNode* root = new TreeNode(*maxIter);
// 左
vector<int> left(nums.begin(), maxIter);
root->left = traversal(left);
// 右
vector<int> right(maxIter + 1, nums.end());
root->right = traversal(right);
return root;
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
TreeNode* root = traversal(nums);
return root;
}
优化写法:
将传入参数变为代表左右区间的迭代器,从原始数组中切片,这样就不需要每次都创建左右子树的序列数组了
TreeNode* traversal(vector<int>::iterator left, vector<int>::iterator right, vector<int> &nums) {
if (right - left == 0)
return nullptr;
// 中,获取序列中的最大值,以此构建子树根节点
// 剪枝,如果数组中只有一个元素则直接返回
if (right - left == 1)
return new TreeNode(*left);
auto maxIter = std::max_element(left, right);
TreeNode* root = new TreeNode(*maxIter);
// 左
root->left = traversal(left, maxIter, nums);
// 右
root->right = traversal(maxIter + 1, right, nums);
return root;
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
TreeNode* root = traversal(nums.begin(), nums.end(), nums);
return root;
}
617.合并二叉树
对两颗树的遍历保持同步即可,与单颗树没太大不同。
自己的写法,太冗余了,优化空间巨大:
TreeNode* traversal(TreeNode* root1, TreeNode* root2) {
// 两个树都为空时该节点才为空
if (!root1 && !root2)
return nullptr;
// 中
int val = 0;
val += root1 ? root1->val : 0;
val += root2 ? root2->val : 0;
TreeNode* root = new TreeNode(val);
// 左、右
if (!root1) {
root->left = traversal(nullptr, root2->left);
root->right = traversal(nullptr, root2->right);
}
else if (!root2) {
root->left = traversal(root1->left, nullptr);
root->right = traversal(root1->right, nullptr);
}
else {
root->left = traversal(root1->left, root2->left);
root->right = traversal(root1->right, root2->right);
}
return root;
}
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
TreeNode* root = traversal(root1, root2);
return root;
}
优化的写法:
· 返回值类型:TreeNode*,返回合并完子树的节点指针
· 传入参数:
TreeNode* root1:树1的指针
TreeNode* root2:树2的指针
· 终止条件:任意一颗树为空时,直接返回另一颗树的指针(无论另一颗树是否为空逻辑都是正确的)
· 单层递归逻辑——前序遍历:
需要处理的只有两块子树都不为空的情况(任一为空都会在终止条件处返回)
中:更新当前子树的值,直接在root1上原地更新即可
左:递归构造左子树,root1原地更新
右:递归构造右子树,root1原地更新
TreeNode* traversal(TreeNode* root1, TreeNode* root2) {
// 某一颗树为空时直接返回另一颗即可
if (!root1)
return root2;
if (!root2)
return root1;
// 需要处理的只有两颗树都不为空的情况
// 只需要在一颗树上原地处理即可,不需要新建节点
// 中
root1->val += root2->val;
// 左
root1->left = traversal(root1->left, root2->left);
// 右
root1->right = traversal(root1->right, root2->right);
return root1;
}
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
TreeNode* root = traversal(root1, root2);
return root;
}
700.二叉搜索树中的搜索
递归法:
按照二叉搜索树的性质选择一边子树进行递归即可
· 返回值类型:无返回值类型,使用引用(或全局变量)记录结果
· 传入参数:
TreeNode* cur:当前节点指针
int& val:搜索的目标值
TreeNode*& ans:搜索成功时用于存储搜索结果的指针,初始化为nullptr
· 终止条件:当节点为空时返回,当已经搜索成功时也直接剪枝返回
· 单层递归逻辑:
应用二叉搜索树的特性,而非前中后序遍历
· 当前节点值等于目标值:记录搜索结果并返回
· 当前节点值大于目标值:递归访问左子树
· 当前节点值小于目标值:递归访问右子树
void traversal(TreeNode* cur, int& val, TreeNode*& ans) {
if (ans || !cur)
return;
if (cur->val == val) {
ans = cur;
return;
}
else if (cur->val > val)
traversal(cur->left, val, ans);
else
traversal(cur->right, val, ans);
}
TreeNode* searchBST(TreeNode* root, int val) {
TreeNode* ans = nullptr;
traversal(root, val, ans);
return ans;
}
迭代法:
本题迭代法更简单(迭代法更能利用二叉搜索树的特性,每次迭代的目的更明确)
TreeNode* searchBST0(TreeNode* root, int val) {
while (root) {
if (root->val == val)
return root;
else if (root->val > val)
root = root->left;
else
root = root->right;
}
return root;
}
98.验证二叉搜索树
本题的“坑”:子树的所有节点都要大于/小于根节点
开始有发现坑,但跨层操作没写明白。后来想到既然左子树都要小于根节点,那么设置一个最大值阈值,随着左转不断更新该阈值不就可以了吗,同理设置一个最小值阈值,每次右转更新该阈值即可。
自己的思路:
· 返回值类型:bool,返回该子树是否是二叉搜索树的验证结果
· 传入参数:
TreeNode* cur:当前节点指针
long maxLimit:最大值阈值,随着左转次数逐渐减小,由于极值测试用例得设置为long
long minLimit:最小值阈值,随着右转次数逐渐增大,由于极值测试用例得设置为long
· 终止条件:当节点为空时返回true,定义上空节点是一颗二叉搜索树。
· 单层递归逻辑——前序遍历:
中:判断当前节点值是否小于最大值阈值,且大于最小值阈值,不符合直接返回false
左:递归验证左子树,传入参数中最大阈值更新为当前节点的值
右:递归验证右子树,传入参数中最小阈值更新为当前节点的值
bool traversal(TreeNode* cur, long maxLimit, long minLimit) {
if (!cur)
return true;
// 中
if (!(cur->val < maxLimit && cur->val > minLimit))
return false;
// 左
bool left = traversal(cur->left, cur->val, minLimit);
// 右
bool right = traversal(cur->right, maxLimit, cur->val);
return left && right;
}
bool isValidBST(TreeNode* root) {
// 要设置好初始上下界,设置太低过不了最值测试用例
bool ans = traversal(root, LONG_MAX, LONG_MIN);
return ans;
}
事实上只需要设置一个阈值就行了:
更好的思路:以中序遍历,如果遍历的序列是递增的,那么该树是二叉搜索树
· 返回值类型:bool,返回该子树是否是二叉搜索树的验证结果
· 传入参数:
TreeNode* cur:当前节点指针
TreeNode*& pre:上一个遍历到节点的指针,使用引用(或一个全局变量)记录
· 终止条件:当节点为空时返回true,定义上空节点是一颗二叉搜索树。
· 单层递归逻辑——中序遍历:
左:递归验证左子树
中:判断当前节点值是否大于上一个节点值,不符合返回false,符合的话更新pre
右:递归验证右子树,此时传入的pre已经指向当前节点了
bool traversal(TreeNode* cur, TreeNode*& pre) {
if (!cur)
return true;
// 左
bool left = traversal(cur->left, pre);
// 中,检测当前节点值是否大于上一个节点的值
// 注意条件是小于等于
if (pre && cur->val <= pre->val)
return false;
else
// 更新pre
pre = cur;
// 右
bool right = traversal(cur->right, pre);
return left && right;
}
bool isValidBST(TreeNode* root) {
long maxVal = LONG_MIN; // 初始值是一个极小值
return traversal(root, maxVal);
}
收获
二叉搜索树的一个重要性质:以中序遍历二叉树,得到的遍历序列一定是单调递增的