灵神【基础算法精讲】视频的个人笔记。
一、视频例题
二叉搜索树:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含 大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
1.1 先序遍历
先判断,再递归。 维护一个区间,结点的值必须在区间内,才是二叉搜索树。
bool preOrder(TreeNode* node, long left, long right) {
if(!node) return true;
return node->val > left && node->val < right
&& preOrder(node->left, left, node->val) //左孩子小于当前节点
&& preOrder(node->right, node->val, right); //右孩子大于当前节点
}
bool isValidBST(TreeNode* root) {
return preOrder(root, LONG_MIN, LONG_MAX);
}
1.2 中序遍历
大于上一个节点。用一个全局变量pre来保存上一个节点的值。
class Solution {
public:
long pre = LONG_MIN;
bool inOrder(TreeNode* node) {
if(!node) return true;
if(! inOrder(node->left)) return false;
if(node->val <= pre) return false;
pre = node->val;
return inOrder(node->right);
}
bool isValidBST(TreeNode* root) {
return inOrder(root);
}
};
if(! inOrder(node->left)) return false;
- 如果左子不成立,该子树不是二叉搜索树,整棵树就不是二叉搜索树。
- 一个条件不成立,能推出整棵树不是二叉搜索树。
这里不能写if(inOrder(node->left)) return true;
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含 大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
只有左子成立,满足了第一个条件,下面两条未知,所以不能推出该子树是二叉搜索树。
1.3 后序遍历
先递归,再判断
节点的值往上传
class Solution {
public:
pair<long, long> postOrder(TreeNode* node) {
if(!node) return {LONG_MAX, LONG_MIN};
pair<long, long> left = postOrder(node->left);
pair<long, long> right = postOrder(node->right);
long v = node->val;
long l_min = left.first, l_max = left.second;
long r_min = right.first, r_max = right.second;
if(v <= l_max || v >= r_min)
return {LONG_MIN, LONG_MAX};
return { min(l_min, v), max(r_max, v) }; //子树的最小值和最大值
}
bool isValidBST(TreeNode* root) {
return postOrder(root).second != LONG_MAX;
}
};
课后作业:
230 二叉搜索树中第K小的元素
给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。
示例 1:
输入:root = [3,1,4,null,2], k = 1
输出:1
中序遍历,就是一个排好序的列表
class Solution {
public:
int count, ans;
void dfs(TreeNode* node) {
if(!node || count <= 0) return;
dfs(node->left);
if(--count == 0) ans = node->val;
dfs(node->right);
}
int kthSmallest(TreeNode* root, int k) {
count = k;
dfs(root);
return ans;
}
};
501 二叉搜索树中的众数
给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。 如果树中有不止一个众数,可以按 任意顺序 返回。 假定 BST 满足如下定义:
- 结点左子树中所含节点的值 小于等于 当前节点的值
- 结点右子树中所含节点的值 大于等于 当前节点的值
- 左子树和右子树都是二叉搜索树
示例 1:
输入:root = [1,null,2,2]
输出:[2]
中序遍历,就是一个排好序的列表,只需要维护这三个变量。
- 当前数
- 当前数的出现次数
- 最大出现次数
class Solution {
public:
int v, cnt = 0, maxCnt = 0;
vector<int> ans;
void find(TreeNode* node) {
if(!node) return;
find(node->left);
if(node->val == v) { //当前计算的数不变
cnt++;
} else { //改变
v = node->val;
cnt = 1;
}
if(cnt > maxCnt) { //超过最大次数
maxCnt = max(maxCnt, cnt); //更新最大次数
ans = vector<int>{ node->val }; //重置
} else if(cnt == maxCnt) { //一样, 加入ans
ans.emplace_back(node->val);
}
find(node->right);
}
vector<int> findMode(TreeNode* root) {
v = root->val;
ans = vector<int>{root->val};
find(root);
return ans;
}
};
530 二叉搜索树的最小绝对差
给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。
差值是一个正数,其数值等于两值之差的绝对值。
输入:root = [4,2,6,1,3]
输出:1
这题跟上一题差不多,也是利用排好序的性质。
最小绝对差肯定在相邻的节点中产生,维护两个变量:
- 最小绝对差
- 上一个节点的值
class Solution {
public:
int diff = INT_MAX, pre = -1;
void find(TreeNode* node) {
if(!node) return;
find(node->left);
if(pre == -1) //pre未用过
pre = node->val;
if(node->val != pre) { //与上一个值不同, 若相同, 差值为0
diff = min(diff, node->val - pre);
pre = node->val;
}
find(node->right);
}
int getMinimumDifference(TreeNode* root) {
find(root);
return diff;
}
};
700 二叉搜索树中的搜索
给定二叉搜索树(BST)的根节点 root 和一个整数值 val。 你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。
示例 1:
输入:root = [4,2,7,1,3], val = 2
输出:[2,1,3]
利用性质,左边小,右边大。
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if(!root) return nullptr;
if(root->val == val)
return root;
else if(root->val < val)
return searchBST(root->right, val);
else
return searchBST(root->left, val);
}
};
1373 二叉搜索子树的最大键值和
给你一棵以 root 为根的 二叉树 ,请你返回 任意 二叉搜索子树的最大键值和。
二叉搜索树的定义如下:
任意节点的左子树中的键值都 小于 此节点的键值。 任意节点的右子树中的键值都 大于 此节点的键值。 任意节点的左子树和右子树都是二叉搜索树。
示例 1:
输入:root = [1,4,3,2,4,2,5,null,null,null,null,null,null,4,6]
输出:20
解释:键值为 3 的子树是和最大的二叉搜索树。
跟视频中讲到的后序遍历,基本一致,多了个最大子树和。
class Solution {
public:
int ans = 0;
tuple<int, int, int> dfs(TreeNode* node) {
if(!node) return {INT_MAX, INT_MIN, 0};
auto [l_min, l_max, l_sum] = dfs(node->left);
auto [r_min, r_max, r_sum] = dfs(node->right);
int v = node->val;
if(v <= l_max || v >= r_min)
return {INT_MIN, INT_MAX, 0};
int sum = l_sum + r_sum + v; //子树和
ans = max(ans, sum);
return { min(v, l_min), max(v, r_max), sum };
}
int maxSumBST(TreeNode* root) {
dfs(root);
return ans;
}
};