灵神【基础算法精讲】视频的个人笔记。
视频讲的题
100. 相同的树 - 力扣(LeetCode)
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(p == nullptr || q == nullptr)
return p == q; //都为空 才会相等, 其中一个为空 不相等
//val和左右子树都相等 为true
return p->val == q->val && isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
};
if(p == nullptr || q == nullptr) 中,有三种情况
p = nullptr,q != nullptr- p、q 结点不相同
q != nullptr,q != nullptr- p、q 结点不相同
p = nullptr并且q = nullptr- p 和 q 相同
如果两棵子树相同,null 也得相同,所有可以直接返回p == q。
101. 对称二叉树 - 力扣(LeetCode)
该题跟上一题很像,可以逐步转换
- 判断 root 的左子树和右子树是否相等。
- 轴对称,判断时反过来,p 的左子树跟 q 的右子树对比,p 的右子树跟 q 的左子树对比。
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(p == nullptr || q == nullptr)
return p == q; //都为空 才会相等, 其中一个为空 不相等
//左右子树反过来
return p->val == q->val && isSameTree(p->left, q->right) && isSameTree(p->right, q->left);
}
bool isSymmetric(TreeNode* root) {
return isSameTree(root->left, root->right);
}
};
110. 平衡二叉树 - 力扣(LeetCode)
该题需要求高度和判断高度平衡
如果不是平衡树,得返回一个不同的值,由于高度不可能有负数,因此返回 -1 作为标识。
class Solution {
public:
int dfs(TreeNode* root) {
if(root == nullptr) return 0;
int lh = dfs(root->left);
if(lh == -1) return -1;
int rh = dfs(root->right);
if(rh == -1) return -1;
return abs(lh - rh) <= 1 ? max(lh, rh) + 1 : -1;
}
bool isBalanced(TreeNode* root) {
return dfs(root) != -1;
}
};
199. 二叉树的右视图 - 力扣(LeetCode)
ans做全局变量,数组的长度作为访问过的节点的最大高度
class Solution {
public:
vector<int> ans;
void dfs(TreeNode* root, int h) {
if(root == nullptr) return;
if(ans.size() == h)
ans.emplace_back(root->val);
dfs(root->right, h + 1); //先右子树
dfs(root->left, h + 1);
}
vector<int> rightSideView(TreeNode* root) {
dfs(root, 0);
return ans;
}
};
课后作业
226. 翻转二叉树 - 力扣(LeetCode)
你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
示例 1:
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
子问题:交换左右子树
class Solution {
public:
void dfs(TreeNode* p) {
if(p == nullptr) return;
dfs(p->left);
dfs(p->right);
//交换
TreeNode* tmp = p->left;
p->left = p->right;
p->right = tmp;
}
TreeNode* invertTree(TreeNode* root) {
if(root == nullptr) return root;
dfs(root);
return root;
}
};
1026. 节点与其祖先之间的最大差值 - 力扣(LeetCode)
给定二叉树的根节点 root,找出存在于 不同 节点 A 和 B 之间的最大值 V,其中 V = |A.val - B.val|,且 A 是 B 的祖先。
(如果 A 的任何子节点之一为 B,或者 A 的任何子节点是 B 的祖先,那么我们认为 A 是 B 的祖先)
示例 1:
输入:root = [8,3,10,1,6,null,14,null,null,4,7,13]
输出:7
解释:
我们有大量的节点与其祖先的差值,其中一些如下:
|8 - 3| = 5
|3 - 7| = 4
|8 - 1| = 7
|10 - 13| = 3
在所有可能的差值中,最大值 7 由 |8 - 1| = 7 得出。
- 维护一条路径上的最大值和最小值。
- 到了终点,为空,更新该路径的最大差值。
class Solution {
public:
int ans = 0;
void dfs(TreeNode* root, int mx, int mn) {
if(root == nullptr) {
ans = max(ans, mx - mn); //终点才更新
return;
}
//最大值和最小值
mx = max(root->val, mx);
mn = min(root->val, mn);
dfs(root->left, mx, mn);
dfs(root->right, mx, mn);
}
int maxAncestorDiff(TreeNode* root) {
dfs(root, root->val, root->val);
return ans;
}
};
1080. 根到叶路径上的不足节点 - 力扣(LeetCode)
给你二叉树的根节点 root 和一个整数 limit ,请你同时删除树中所有 不足节点 ,并返回最终二叉树的根节点。
假如通过节点 node 的每种可能的 “根-叶” 路径上值的总和全都小于给定的 limit,则该节点被称之为 不足节点 ,需要被删除。
叶子节点,就是没有子节点的节点。
示例 1:
输入:root = [1,2,3,4,-99,-99,7,8,9,-99,-99,12,13,-99,14], limit = 1
输出:[1,2,3,4,null,null,7,8,9,null,14]
节点有两种情况
- 叶子节点:只有一条路径,该路径和小于
limit就删除。 - 非叶子节点:通向左子树的路径 和 通向右子树的路径。
- 子树没有被删完:不删该节点
- 子树都被删完了:删除该节点
非叶子节点
要删除非叶节点 node,当且仅当 node 的所有儿子都被删除。
flowchart TB
1-->3 --> -99 & 7
-99 --> 12 & 13
7 --> A[-99] & 14
子树没有被删完
对于7这个节点,通过节点的路径有两条
- 1 --> 3 --> 7 --> -99
- 1 --> 3 --> 7 --> 14
先删除叶子节点:
1到-99这条路径和总和小于1,叶子节点-99为不足节点,删除。1到14这条路径和总和大于1,叶子节点14不需要删除。
flowchart TB
1-->3 --> -99 & 7
-99 --> 12 & 13
7 --> 14
假如通过节点
node的每种可能的 “根-叶” 路径上值的总和全都小于给定的limit,则该节点被称之为 不足节点 ,需要被删除。
这时对于节点7,==子树没有被删完==,其中一种可能的 “根-叶” 路径上值的总和大于1,所以节点7不用删除。
子树都被删完
对于-99这个节点,通过节点的路径有两条
- 1 --> 3 --> -99 --> 12
- 1 --> 3 --> -99 --> 13
先删除叶子节点:
1到12和1到13的两条路径和总和都是小于1的,叶子节点12和13为不足节点,都要删除。
flowchart TB
1-->3 --> -99 & 7
7 --> 14
假如通过节点
node的每种可能的 “根-叶” 路径上值的总和全都小于给定的limit,则该节点被称之为 不足节点 ,需要被删除。
这时对于节点-99,子树都被删完了,因为通过节点 -99 的每种可能的 “根-叶” 路径上值的总和全都小于1,所以-99节点也是不足节点,故也得删除。(注意是根-叶路径)
flowchart TB
1-->3 --> 7
7 --> 14
class Solution {
public:
TreeNode* sufficientSubset(TreeNode* root, int limit) {
if(root == nullptr) return nullptr;
limit -= root->val;
if(root->left == nullptr && root->right == nullptr) //叶子结点
return 0 < limit ? nullptr : root;
root->left = sufficientSubset(root->left, limit);
root->right = sufficientSubset(root->right, limit);
//如果还有儿子,就不删 p,否则删 p
return root->left || root->right ? root : nullptr;
}
};
1110. 删点成林 - 力扣(LeetCode)
给出二叉树的根节点 root,树上每个节点都有一个不同的值。
如果节点值在 to_delete 中出现,我们就把该节点从树上删去,最后得到一个森林(一些不相交的树构成的集合)。
返回森林中的每棵树。你可以按任意顺序组织答案。
示例 1:
输入:root = [1,2,3,4,5,6,7], to_delete = [3,5]
输出:[[1,2,null,4],[6],[7]]
这题跟上一题的思路基本一样,归的时候返回null来删除节点。
class Solution {
public:
vector<TreeNode*> ans;
TreeNode* dfs(TreeNode* p, vector<int>& to_delete) {
if(p == nullptr) return nullptr;
p->left = dfs(p->left, to_delete);
p->right = dfs(p->right, to_delete);
for(int i: to_delete) {
if(p->val == i) {
//左右子树,有就加入ans
if(p->left) ans.emplace_back(p->left);
if(p->right) ans.emplace_back(p->right);
return nullptr; //删除该节点
}
}
return p; //不删除,返回
}
vector<TreeNode*> delNodes(TreeNode* root, vector<int>& to_delete) {
TreeNode* r = dfs(root, to_delete);
if(r != nullptr) ans.emplace_back(r);
return ans;
}
};
1372. 二叉树中的最长交错路径 - 力扣(LeetCode)
给你一棵以 root 为根的二叉树,二叉树中的交错路径定义如下:
- 选择二叉树中 任意 节点和一个方向(左或者右)。
- 如果前进方向为右,那么移动到当前节点的的右子节点,否则移动到它的左子节点。
- 改变前进方向:左变右或者右变左。
- 重复第二步和第三步,直到你在树中无法继续移动。
交错路径的长度定义为:访问过的节点数目 - 1(单个节点的路径长度为 0 )。
请你返回给定树中最长 交错路径 的长度。
示例 1:
输入:root = [1,null,1,1,1,null,null,1,1,null,1,null,null,null,1,null,1]
输出:3
解释:蓝色节点为树中最长交错路径(右 -> 左 -> 右)。
- 维护一个全局变量:最长交错路径的长度。
#define turn_left 0
#define turn_right 1
class Solution {
public:
int maxLen = 0;
void dfs(TreeNode* p, int len, int turn) {
if(!p) return;
maxLen = max(maxLen, len);
if(turn == turn_left) { //上一次左转
dfs(p->left, 1, turn_left); //方向未改变,重算长度
dfs(p->right, len + 1, turn_right); //方向改变,+1
} else { //上一次右转
dfs(p->left, len + 1, turn_left);
dfs(p->right, 1, turn_right);
}
}
int longestZigZag(TreeNode* root) {
if(!root) return 0;
dfs(root->left, 1, turn_left);
dfs(root->right, 1, turn_right);
return maxLen;
}
};