由前中后序遍历构造二叉树
问题分类
- 根据前序遍历和中序遍历构造二叉树:前序遍历的头节点一定是根节点,从前序遍历中找到根节点并在中序遍历中找到根节点对应的位置,将中序遍历数组分为两部分。
设前序遍历数组区间为[pl, pr],中序遍历数组区间为[il, ir],设根节点在中序遍历数组中下标为t。 递归终止条件为:pl > pr 逻辑:左儿子根节点为递归前序数组[pl+1, t-il+pl],对应中序数组[il, t-1],右儿子根节点为递归前序数组[t-il+pl+1, pr-1],对应中序数组[t+1, ir] - 根据中序遍历和后序遍历构造二叉树:后序遍历的尾节点一定是根节点,从后序遍历中找到根节点并在中序遍历中找到根节点对应的位置,将中序遍历数组分为两部分。
设前序遍历数组区间为[pl, pr],中序遍历数组区间为[il, ir],设根节点在中序遍历数组中下标为t。 递归终止条件为:pl > pr 逻辑:左儿子根节点为递归后序数组[pl, t-il+pl-1],对应中序数组[il, t-1],右儿子根节点为递归后序数组[t-il+pl, pr-1],对应中序数组[t+1, ir]
leetcode.105
- 链接leetcode.cn/problems/co…
- 解题方法:用哈希表存中序遍历中根节点的位置
将根节点创建出来,用上述思路解题 - leetcode解题代码
class Solution {
public:
unordered_map<int, int> hash;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = preorder.size();
for (int i = 0; i < n; i ++) hash[inorder[i]] = i;
return build(preorder, inorder, 0, n - 1, 0, n - 1);
}
TreeNode* build(vector<int>& preorder, vector<int>& inorder, int pl, int pr, int il, int ir){
if (pl > pr) return nullptr;
auto t = hash[preorder[pl]];
auto root = new TreeNode(preorder[pl]);
root->left = build(preorder, inorder, pl + 1, t - il + pl, il, t - 1);
root->right = build(preorder, inorder, t - il + pl + 1, pr, t + 1, ir);
return root;
}
};
leetcode.106
- 链接leetcode.cn/problems/co…
- 解题方法:用哈希表存中序遍历中根节点的位置
将根节点创建出来,用上述思路解题 - leetcode解题代码
class Solution {
public:
unordered_map<int, int> hash;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = preorder.size();
for (int i = 0; i < n; i ++) hash[inorder[i]] = i;
return build(preorder, inorder, 0, n - 1, 0, n - 1);
}
TreeNode* build(vector<int>& preorder, vector<int>& inorder, int pl, int pr, int il, int ir){
if (pl > pr) return nullptr;
auto t = hash[preorder[pl]];
auto root = new TreeNode(preorder[pl]);
root->left = build(preorder, inorder, pl + 1, t - il + pl, il, t - 1);
root->right = build(preorder, inorder, t - il + pl + 1, pr, t + 1, ir);
return root;
}
};
构造二叉树
leetcode.654
- 链接leetcode.cn/problems/ma…
- 解题方法:
在数组中找到最大的数定义为根节点,且不能改变数组中数的顺序(双指针)
根节点的左儿子为递归根节点的左半部分
根节点的右儿子为递归根节点的右半部分 - leetcode解题代码
class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
int n = nums.size();
return dfs(nums, 0, n - 1);
}
TreeNode* dfs(vector<int> nums, int l, int r){
if (l > r) return nullptr;
int pos = l;
for (int i = l + 1; i <= r; i ++){
if (nums[i] > nums[pos])
pos = i;
}
TreeNode* root = new TreeNode(nums[pos]);
root->left = dfs(nums, l, pos - 1);
root->right = dfs(nums, pos + 1, r);
return root;
}
};
leetcode.617
- 链接leetcode.cn/problems/me…
- 解题方法:
特判:如果root1或root2不存在直接返回存在的树
逻辑:根节点相加返回新的根节点
左儿子相加返回新的左儿子,右儿子相加返回新的右儿子
返回值:返回新树的根节点 - leetcode解题代码
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if (!root1) return root2;
if (!root2) return root1;
root1->val += root2->val;
root1->left = mergeTrees(root1->left, root2->left);
root1->right = mergeTrees(root1->right, root2->right);
return root1;
}
};
leetcode.108
- 链接leetcode.cn/problems/co…
- 解题方法:
构造二叉搜索树的根节点(升序数组的中点)
递归中点的左右两端构造左子树和右子树 - leetcode解题代码
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
auto root = dfs(nums, 0, nums.size() - 1);
return root;
}
TreeNode* dfs(vector<int> nums, int l, int r){
if (l > r) return nullptr;
// 二叉搜索树为升序排列,中点即为根节点
int mid = (l + r) / 2;
TreeNode* root = new TreeNode(nums[mid]);
root->left = dfs(nums, l, mid - 1);
root->right = dfs(nums, mid + 1, r);
return root;
}
};
leetcode.538
- 链接leetcode.cn/problems/co…
- 解题方法:
找到比根节点大的数,即二叉搜索树的右子树
求和并更新根节点的值 - leetcode解题代码
class Solution {
public:
int sum = 0;
TreeNode* bstToGst(TreeNode* root) {
if (!root) return nullptr;
// 找到比根节点大的数,即二叉搜索树的右子树
bstToGst(root->right);
// 求和并更新根节点的值
sum += root->val;
root->val = sum;
bstToGst(root->left);
return root;
}
};
最近公共祖先
leetcode.236
- 链接leetcode.cn/problems/lo…
- 解题方法:
特判:如果根节点不存在或者根节点和p或q相等,那么根节点就是最近公共祖先
逻辑:找到左子树的最近公共祖先,找到右子树的最近公共祖先
如果左右子树的最近公共祖先都存在,返回根节点
如果左子树最近公共祖先不存在,返回右子树的最近公共祖先
如果右子树最近公共祖先不存在,返回左子树的最近公共祖先
返回值:返回新树的根节点 - leetcode解题代码
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (!root || root == p || root == q) return root;
auto left = lowestCommonAncestor(root->left, p, q);
auto right = lowestCommonAncestor(root->right, p, q);
if (left && right) return root;
if (!left) return right;
return left;
}
};
leetcode.235
- 链接leetcode.cn/problems/lo…
- 解题方法:
二叉搜索树的性质:左儿子<根节点<右儿子
特判:如果根节点不存在则返回根节点,由于二叉搜索树的性质左右儿子不可能等于根节点的值
逻辑:如果p,q的值均小于根节点,说明他们的最近公共祖先在左子树,递归左子树;如果p,q的值均大于根节点,说明他们的最近公共祖先在右子树,递归右子树;如果p,q在根节点两侧他们的最近公共祖先就是根节点 - leetcode解题代码
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (!root || root == p || root == q) return root;
auto left = lowestCommonAncestor(root->left, p, q);
auto right = lowestCommonAncestor(root->right, p, q);
if (left && right) return root;
if (!left) return right;
return left;
}
};
二叉搜索树(查找、插入、删除)
解题方法
二叉搜索树的性质
- 若左子树不空,那左子树所有节点的值均 < 根节点的值
- 若右子树不空,那右子树所有节点的值均 > 根节点的值
- 左右子树也均为二叉搜索树
所以二叉搜索树的中序遍历是一个有序序列
查询
leetcode.700
- 链接leetcode.cn/problems/se…
- 解题方法:
二叉搜索树的性质:左儿子<根节点<右儿子
特判:如果根节点不存在则返回空,如果根节点的值等于val则返回根节点
逻辑:定义答案根节点,如果根节点的值大于val则递归左子树,如果根节点的值小于val则递归右子树 - leetcode解题代码
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if (!root) return nullptr;
if (root->val == val) return root;
TreeNode* res = nullptr;
if (root->val > val) res = searchBST(root->left, val);
if (root->val < val) res = searchBST(root->right, val);
return res;
}
};
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if (!root) return nullptr;
while (root){
if (root->val == val) return root;
else if (root->val > val) root = root->left;
else root = root->right;
}
return root;
}
};
leetcode.98
- 链接leetcode.cn/problems/va…
- 解题方法:
判断中序遍历是否有序 - leetcode解题代码
class Solution {
public:
vector<int> res;
bool isValidBST(TreeNode* root) {
dfs(root, res);
for (int i = 1; i <= res.size(); i ++){
if (res[i] <= res[i - 1])
return false;
}
return true;
}
void dfs(TreeNode* root, vector<int> res){
if (!root) return;
dfs(root->left, res);
res.push_back(root->val);
dfs(root->right, res);
}
};
class Solution {
public:
TreeNode* pre = nullptr;
bool isValidBST(TreeNode* root) {
stack<TreeNode*> stk;
while (root || stk.size()){
while (root){
stk.push(root);
root = root->left;
}
auto cur = stk.top();
stk.pop();
if (pre && cur->val <= pre->val) return false;
pre = cur;
root = cur->right;
}
return true;
}
};
leetcode.530
- 链接leetcode.cn/problems/mi…
- 解题方法:中序遍历递归,中序遍历迭代
- leetcode解题代码
// 递归
class Solution {
public:
int res = INT_MAX;
TreeNode* pre = NULL;
int getMinimumDifference(TreeNode* root) {
dfs(root);
return res;
}
void dfs(TreeNode* cur){
if (!cur) return;
dfs(cur->left);
// 如果上一个节点的值存在,则求差
if (pre){
res = min(res, cur->val - pre->val);
}
// 否则将当前节点的值赋值为上一个节点
pre = cur;
dfs(cur->right);
}
};
// 迭代
class Solution {
public:
int getMinimumDifference(TreeNode* root) {
stack<TreeNode*> stk;
int res = INT_MAX;
TreeNode* pre = nullptr;
while (root || stk.size()){
while (root){
stk.push(root);
root = root->left;
}
auto cur = stk.top();
stk.pop();
if (pre)
res = min(cur->val - pre->val, res);
pre = cur;
root = cur->right;
}
return res;
}
};
leetcode.501
- 链接leetcode.cn/problems/fi…
- 解题方法:中序遍历递归,中序遍历迭代
- leetcode解题代码
class Solution {
public:
vector<int> res;
int curc = 0, maxc = 0, pre;
vector<int> findMode(TreeNode* root) {
dfs(root);
return res;
}
void dfs(TreeNode* root){
if (!root) return;
dfs(root->left);
// 如果当前节点的值等于上一个节点
if (root->val == pre){
// 当前计数+1
curc ++;
// 更新上一个节点
pre = root->val;
}
// 如果值不相等
else{
// 当前为第一个数
curc = 1;
// 更新上一个节点
pre = root->val;
}
// 如果当前计数值大于最大个数值
if (curc > maxc){
// 更新最大个数
maxc = curc;
// 将上一个节点加入答案
res = {pre};
}
// 如果出现不止一个众数,加入答案
else if (curc == maxc) res.push_back(pre);
dfs(root->right);
}
};
class Solution {
public:
vector<int> res;
int curc = 0, maxc = 0, pre;
vector<int> findMode(TreeNode* root) {
stack<TreeNode*> stk;
while (root || stk.size()){
while (root){
stk.push(root);
root = root->left;
}
auto cur = stk.top();
stk.pop();
if (cur->val == pre){
curc ++;
pre = cur->val;
}
else{
curc = 1;
pre = cur->val;
}
if (curc > maxc){
maxc = curc;
res = {pre};
}
else if (curc == maxc) res.push_back(pre);
root = cur->right;
}
return res;
}
};
插入
leetcode.701
- 链接leetcode.cn/problems/in…
- 解题方法:查找插入的值所在的位置,如果头节点不存在则返回新头节点,如果val大于根节点,递归右子树找插入的位置,如果val小于根节点,递归左子树找插入的位置
- leetcode解题代码
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (!root) return new TreeNode(val);
if (root->val > val) root->left = insertIntoBST(root->left, val);
else root->right = insertIntoBST(root->right, val);
return root;
}
};
删除
leetcode.450
- 链接leetcode.cn/problems/de…
- 解题方法:题解
- leetcode解题代码
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (!root) return nullptr;
if (root->val > key) root->left = deleteNode(root->left, key);
else if (root->val < key) root->right = deleteNode(root->right, key);
else {
// key为叶子节点
if (!root->left && !root->right){
root = nullptr;
return root;
}
// key只有右子树
else if (!root->left && root->right) return root->right;
// key只有左子树
else if (root->left && !root->right) return root->left;
// key左右子树都有
// 找到右子树中左下角的儿子,将当前节点的左儿子接到右子树左下角的儿子上
else {
TreeNode* cur = root->right;
while (cur->left){
cur = cur->left;
}
cur->left = root->left;
return root->right;
}
}
return root;
}
};
修改
leetcode.669
- 链接leetcode.cn/problems/tr…
- 解题方法:题解
- leetcode解题代码
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if (!root) return NULL;
// 如果根节点不再[low, high]的范围内,递归找到根节点
if (root->val < low) return trimBST(root->right, low, high);
if (root->val > high) return trimBST(root->left, low, high);
// 找到根节点,递归找到左右子树
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
return root;
}
};