每日算法刷题Day75:10.18:leetcode 二叉树14道题,用时3h
九、二叉搜索树
1.套路
1.一般是中序遍历,可以得到有序数组,代码中常用一个pre
变量记录前一元素。
2.二叉搜索树满足如下性质:
- 左子树所有节点的元素值均小于根的元素值;
- 右子树所有节点的元素值均大于根的元素值。
3.代码模版:
class Solution {
public:int res = INT_MAX;int pre = INT_MIN / 2; // 为了防止减法溢出void dfs(TreeNode* cur) {if (cur == nullptr)return;dfs(cur->left);// res = min(res, cur->val - pre); // 这一行可以替换pre = cur->val;dfs(cur->right);}int minDiffInBST(TreeNode* root) {dfs(root);return res;}
};
2.题目描述
3.学习经验
1. 700. 二叉搜索树中的搜索(简单)
700. 二叉搜索树中的搜索 - 力扣(LeetCode)
思想
1.给定二叉搜索树(BST)的根节点 root
和一个整数值 val
。
你需要在 BST 中找到节点值等于 val
的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null
。
2.根据二叉搜索树性质,得到如下算法:
- 若 root 为空则返回空节点;
- 若 val=root.val,则返回 root;
- 若 val<root.val,递归左子树;
- 若 val>root.val,递归右子树。
代码
class Solution {
public:TreeNode* searchBST(TreeNode* root, int val) {if (root == nullptr || root->val == val)return root;if (root->val < val)return searchBST(root->right, val);if (root->val > val)return searchBST(root->left, val);return nullptr;}
};
2. 530. 二叉搜索树的最小绝对差(简单,学习)
530. 二叉搜索树的最小绝对差 - 力扣(LeetCode)
思想
1.给你一个二叉搜索树的根节点 root
,返回 树中任意两不同节点值之间的最小差值 。
差值是一个正数,其数值等于两值之差的绝对值。
2.简洁写法是中序遍历,同时拿一个pre
遍历记录中序遍历数组前一个数
代码
我的代码:
class Solution {
public:typedef pair<int, int> PII;int res = INT_MAX;PII dfs(TreeNode* cur) { // {min,max}if (cur->left == nullptr && cur->right == nullptr) {return {cur->val, cur->val};} else if (cur->left == nullptr && cur->right != nullptr) {PII r = dfs(cur->right);int rMin = r.first, rMax = r.second;res = min(res, rMin - cur->val);return {cur->val, rMax};} else if (cur->left != nullptr && cur->right == nullptr) {PII l = dfs(cur->left);int lMin = l.first, lMax = l.second;res = min(res, cur->val - lMax);return {lMin, cur->val};} else {PII l = dfs(cur->left), r = dfs(cur->right);int rMin = r.first, rMax = r.second;int lMin = l.first, lMax = l.second;res = min(res, cur->val - lMax);res = min(res, rMin - cur->val);return {lMin, rMax};}}int getMinimumDifference(TreeNode* root) {if (root == nullptr)return 0;dfs(root);return res;}
};
中序遍历:
class Solution {
public:int res = INT_MAX;int pre = INT_MIN / 2; // 防止减法溢出(第一次的时候)void dfs(TreeNode* cur) {if (cur == nullptr)return;dfs(cur->left);res = min(res, cur->val - pre);pre = cur->val;dfs(cur->right);}int getMinimumDifference(TreeNode* root) {dfs(root);return res;}
};
3. 783. 二叉搜索树节点最小距离(简单)
783. 二叉搜索树节点最小距离 - 力扣(LeetCode)
思想
1.给你一个二叉搜索树的根节点 root
,返回 树中任意两不同节点值之间的最小差值 。
差值是一个正数,其数值等于两值之差的绝对值。
2.和[[十五.二叉树#2. 530. 二叉搜索树的最小绝对差(简单,学习)]]一样
代码
class Solution {
public:int res = INT_MAX;int pre = INT_MIN / 2;void dfs(TreeNode* cur) {if (cur == nullptr)return;dfs(cur->left);res = min(res, cur->val - pre);pre = cur->val;dfs(cur->right);}int minDiffInBST(TreeNode* root) {dfs(root);return res;}
};
4. 938. 二叉搜索树的范围和(简单)
思想
1.给定二叉搜索树的根结点 root
,返回值位于范围 [low, high]
之间的所有结点的值的和。
2.可以中序遍历,剪枝>high
的部分
3.也可以直接依据二叉搜索树的性质决定递归左子树还是右子树,剪枝<low
和>high
的部分
代码
我的代码:
class Solution {
public:int res = 0;void dfs(TreeNode* cur, int low, int high) {if (cur == nullptr)return;dfs(cur->left, low, high);if (cur->val >= low && cur->val <= high)res += cur->val;if (cur->val > high)return; // 剪枝dfs(cur->right, low, high);}int rangeSumBST(TreeNode* root, int low, int high) {dfs(root, low, high);return res;}
};
学习:
class Solution {
public:int rangeSumBST(TreeNode* root, int low, int high) {if (root == nullptr)return 0;int x = root->val;// 剪枝x<lowif (x < low)return rangeSumBST(root->right, low, high);// 剪枝x>highif (x > high)return rangeSumBST(root->left, low, high);return x + rangeSumBST(root->left, low, high) +rangeSumBST(root->right, low, high);}
};
5. 501. 二叉搜索树中的众数(简单)
501. 二叉搜索树中的众数 - 力扣(LeetCode)
思想
1.给你一个含重复值的二叉搜索树(BST)的根节点 root
,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
假定 BST 满足如下定义:
- 结点左子树中所含节点的值 小于等于 当前节点的值
- 结点右子树中所含节点的值 大于等于 当前节点的值
- 左子树和右子树都是二叉搜索树
代码
class Solution {
public:map<int, int> valToCnt;vector<int> res;int maxCnt = INT_MIN;void dfs(TreeNode* cur) {if (cur == nullptr)return;dfs(cur->left);++valToCnt[cur->val];if (valToCnt[cur->val] > maxCnt) {res.clear();res.push_back(cur->val);maxCnt = valToCnt[cur->val];} else if (valToCnt[cur->val] == maxCnt) {res.push_back(cur->val);}dfs(cur->right);}vector<int> findMode(TreeNode* root) {dfs(root);return res;}
};
6. 230. 二叉搜索树中第K小的元素(中等)
230. 二叉搜索树中第 K 小的元素 - 力扣(LeetCode)
思想
1.给定一个二叉搜索树的根节点 root
,和一个整数 k
,请你设计一个算法查找其中第 k
小的元素(从 1 开始计数)。
代码
class Solution {
public:int res;int cnt = 0;void dfs(TreeNode* cur, int k) {if (cur == nullptr)return;dfs(cur->left, k);++cnt;if (cnt == k)res = cur->val;else if (cnt > k)return;dfs(cur->right, k);}int kthSmallest(TreeNode* root, int k) {dfs(root, k);return res;}
};
7. 98. 验证二叉搜索树(中等)
98. 验证二叉搜索树 - 力扣(LeetCode)
思想
1.给你一个二叉树的根节点 root
,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
- 节点的左子树只包含 严格小于 当前节点的数。
- 节点的右子树只包含 严格大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
2.不用显示数组,还是用一个pre
变量
代码
class Solution {
public:vector<int> vec;bool isValidBST(TreeNode* root) {if (root == nullptr)return true;bool isL = isValidBST(root->left);if (!isL)return false;if (!vec.empty() && vec.back() >= root->val)return false;vec.push_back(root->val);bool isR = isValidBST(root->right);if (!isR)return false;return true;}
};
pre
变量:
class Solution {
public:typedef long long ll;ll pre = LLONG_MIN;bool isValidBST(TreeNode* root) {if (root == nullptr)return true;if (!isValidBST(root->left))return false;if (pre != LLONG_MIN && pre >= root->val)return false;pre = root->val;if (!isValidBST(root->right))return false;return true;}
};
8. 1305. 两棵二叉搜索树中的所有元素(中等)
1305. 两棵二叉搜索树中的所有元素 - 力扣(LeetCode)
思想
1.给你 root1
和 root2
这两棵二叉搜索树。请你返回一个列表,其中包含 两棵树 中的所有整数并按 升序 排序。.
代码
class Solution {
public:vector<int> vec1, vec2;void dfs(TreeNode* cur, vector<int>& vec) {if (cur == nullptr)return;dfs(cur->left, vec);vec.push_back(cur->val);dfs(cur->right, vec);}vector<int> getAllElements(TreeNode* root1, TreeNode* root2) {dfs(root1, vec1);dfs(root2, vec2);vector<int> res;int id1 = 0, id2 = 0, n1 = vec1.size(), n2 = vec2.size();while (id1 < n1 && id2 < n2) {if (vec1[id1] <= vec2[id2]) {res.push_back(vec1[id1]);++id1;} else {res.push_back(vec2[id2]);++id2;}}while (id1 < n1) {res.push_back(vec1[id1]);++id1;}while (id2 < n2) {res.push_back(vec2[id2]);++id2;}return res;}
};
9. 99. 恢复二叉搜索树(中等,学习)
99. 恢复二叉搜索树 - 力扣(LeetCode)
思想
1.给你二叉搜索树的根节点 root
,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树 。
2.先搜索,再交换
3.搜索本质是在一个有序数组中找到这两个数,用一个pre
变量来记录前一个数,x
记录第一个不满足条件的pre
,y
记录最后的最小的数(需持续更新)
代码
class Solution {
public:TreeNode *x = nullptr, *y = nullptr, *pre = nullptr;void dfs(TreeNode* cur) {if (cur == nullptr)return;dfs(cur->left);if (pre != nullptr && pre->val > cur->val) {y = cur; // y一直更新到最小的cur->valif (x == nullptr)x = pre; // x为第一次遇见的pre}pre = cur;dfs(cur->right);}void recoverTree(TreeNode* root) {dfs(root);swap(x->val, y->val);}
};
10. 897. 递增顺序搜索树(简单)
897. 递增顺序搜索树 - 力扣(LeetCode)
思想
1.给你一棵二叉搜索树的 root
,请你 按中序遍历 将其重新排列为一棵递增顺序搜索树,使树中最左边的节点成为树的根节点,并且每个节点没有左子节点,只有一个右子节点。
代码
class Solution {
public:TreeNode *pre = nullptr, *newRoot = nullptr;void dfs(TreeNode* cur) {if (cur == nullptr)return;dfs(cur->left);if (pre != nullptr) {cur->left = nullptr;pre->right = cur;} else {newRoot = cur;}pre = cur;dfs(cur->right);}TreeNode* increasingBST(TreeNode* root) {dfs(root);return newRoot;}
};
11. 2476. 二叉搜索树最近节点查询(中等)
2476. 二叉搜索树最近节点查询 - 力扣(LeetCode)
思想
1.给你一个 二叉搜索树 的根节点 root
,和一个由正整数组成、长度为 n
的数组 queries
。
请你找出一个长度为 n
的 二维 答案数组 answer
,其中 answer[i] = [mini, maxi]
:
mini
是树中小于等于queries[i]
的 最大值 。如果不存在这样的值,则使用-1
代替。maxi
是树中大于等于queries[i]
的 最小值 。如果不存在这样的值,则使用-1
代替。
返回数组answer
。
代码
class Solution {
public:vector<int> vec;void dfs(TreeNode* cur) {if (cur == nullptr)return;dfs(cur->left);vec.push_back(cur->val);dfs(cur->right);}vector<vector<int>> closestNodes(TreeNode* root, vector<int>& queries) {dfs(root);int n = vec.size();vector<vector<int>> res;for (auto& x : queries) {int l = 0, r = vec.size() - 1, targetId = vec.size();while (l <= r) {int mid = l + ((r - l) >> 1);if (vec[mid] >= x) {targetId = mid;r = mid - 1;} elsel = mid + 1;}if (targetId == vec.size())res.push_back({vec.back(), -1});else {if (vec[targetId] == x)res.push_back({x, x});else {if (targetId == 0)res.push_back({-1, vec[targetId]});elseres.push_back({vec[targetId - 1], vec[targetId]});}}}return res;}
};
12. 653. 两数之后IV-输入二叉搜索树(简单)
653. 两数之和 IV - 输入二叉搜索树 - 力扣(LeetCode)
思想
1.给定一个二叉搜索树 root
和一个目标结果 k
,如果二叉搜索树中存在两个元素且它们的和等于给定的目标结果,则返回 true
。
代码
class Solution {
public:set<int> st;int pre = INT_MIN;bool res;void dfs(TreeNode* cur, int k) {if (cur == nullptr)return;dfs(cur->left, k);if (st.count(k - cur->val))res = true;st.insert(cur->val);if (res)return;dfs(cur->right, k);}bool findTarget(TreeNode* root, int k) {dfs(root, k);return res;}
};
十、创建二叉树
1.套路
1.大多数题给定数组构造二叉树,本质上是通过查找(可能是二分)获取当前根节点下面,然后左侧数组递归,右侧数组递归,所以递归参数传递当前数组左右边界
2.题目描述
3.学习经验
1. 108. 将有序数组转换为二叉搜索树(简单,学习)
108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)
思想
1.给你一个整数数组 nums
,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。
2.用左右边界递归,而不是把左数组和右数组作为参数传递
代码
class Solution {
public:TreeNode* build(vector<int>& nums, int l, int r) {// [l,r]if (r < l)return nullptr;int mid = l + ((r - l) >> 1);// [l,mid),[mid+1,r]return new TreeNode(nums[mid], build(nums, l, mid - 1),build(nums, mid + 1, r));}TreeNode* sortedArrayToBST(vector<int>& nums) {return build(nums, 0, nums.size() - 1);}
};
2. 654. 最大二叉树(中等)
654. 最大二叉树 - 力扣(LeetCode)
思想
1.给定一个不重复的整数数组 nums
。 最大二叉树 可以用下面的算法从 nums
递归地构建:
- 创建一个根节点,其值为
nums
中的最大值。 - 递归地在最大值 左边 的 子数组前缀上 构建左子树。
- 递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回nums
构建的 最大二叉树 。
代码
class Solution {
public:TreeNode* bulid(vector<int>& nums,int l,int r){// [l,r]if(r<l) return nullptr;int maxId=l;for(int i=l;i<=r;++i){if(nums[i]>nums[maxId]) maxId=i;}// [l,maxId),[maxId+1,r]return new TreeNode(nums[maxId],bulid(nums,l,maxId-1),bulid(nums,maxId+1,r));}TreeNode* constructMaximumBinaryTree(vector<int>& nums) {return bulid(nums,0,nums.size()-1);}
};
3. 998. 最大二叉树II(中等)
998. 最大二叉树 II - 力扣(LeetCode)
思想
1.最大树 定义:一棵树,并满足:其中每个节点的值都大于其子树中的任何其他值。
给你最大树的根节点 root
和一个整数 val
。
就像 之前的问题 那样,给定的树是利用 Construct(a)
例程从列表 a
(root = Construct(a)
)递归地构建的:
- 如果
a
为空,返回null
。 - 否则,令
a[i]
作为a
的最大元素。创建一个值为a[i]
的根节点root
。 root
的左子树将被构建为Construct([a[0], a[1], ..., a[i - 1]])
。root
的右子树将被构建为Construct([a[i + 1], a[i + 2], ..., a[a.length - 1]])
。- 返回
root
。
请注意,题目没有直接给出a
,只是给出一个根节点root = Construct(a)
。
假设b
是a
的副本,并在末尾附加值val
。题目数据保证b
中的值互不相同。
返回Construct(b)
。
2.由例子可以看出:
a = [2,1,5,4], b = [2,1,5,4,3]
就是在数组后面插入一个数val
,然后来更新树,再由最大树构造性质,只需判断根节点和右子树
代码
class Solution {
public:TreeNode* insertIntoMaxTree(TreeNode* root, int val) {if (root == nullptr)return new TreeNode(val);if (root->val < val) {return new TreeNode(val, root, nullptr);}TreeNode* newR = insertIntoMaxTree(root->right, val);root->right = newR; // 更新右子树return root;}
};
4. 1008. 前序遍历构造二叉搜索树(中等)
1008. 前序遍历构造二叉搜索树 - 力扣(LeetCode)
思想
代码
class Solution {
public:TreeNode* build(vector<int>& preorder, int l, int r) {// [l,r]if (l > r)return nullptr;int tmpL = l, tmpR = r, lEnd = r + 1;// 查找while (tmpL <= tmpR) {int mid = tmpL + ((tmpR - tmpL) >> 1);if (preorder[mid] > preorder[l]) {lEnd = mid;tmpR = mid - 1;} elsetmpL = mid + 1;}// [l+1,lEnd),[lEnd,r]return new TreeNode(preorder[l], build(preorder, l + 1, lEnd - 1),build(preorder, lEnd, r));}TreeNode* bstFromPreorder(vector<int>& preorder) {return build(preorder, 0, preorder.size() - 1);}
};