letcode第85场双周赛&第307场周赛(Easy+Medium)
第85场双周赛
6156. 得到 K 个黑块的最少涂色次数
难度简单
给你一个长度为 n 下标从 0 开始的字符串 blocks ,blocks[i] 要么是 'W' 要么是 'B' ,表示第 i 块的颜色。字符 'W' 和 'B' 分别表示白色和黑色。
给你一个整数 k ,表示想要 连续 黑色块的数目。
每一次操作中,你可以选择一个白色块将它 涂成 黑色块。
请你返回至少出现 一次 连续 k 个黑色块的 最少 操作次数。
示例 1:
输入:blocks = "WBBWWBBWBW", k = 7
输出:3
解释:
一种得到 7 个连续黑色块的方法是把第 0 ,3 和 4 个块涂成黑色。
得到 blocks = "BBBBBBBWBW" 。
可以证明无法用少于 3 次操作得到 7 个连续的黑块。
所以我们返回 3 。
示例 2:
输入:blocks = "WBWBBBW", k = 2
输出:0
解释:
不需要任何操作,因为已经有 2 个连续的黑块。
所以我们返回 0 。
提示:
n == blocks.length1 <= n <= 100blocks[i]要么是'W',要么是'B'。1 <= k <= n
滑动窗口
最少涂色次数等同于最少白色块数,在满足题目条件的情况下,找出窗口内最少白色块数的情况即可。
class Solution {
public:
int minimumRecolors(string blocks, int k) {
int ans = INT_MAX;
int num = 0;
int r = -1, l = 0;
int len = blocks.size();
while (r < len-1)//注意此处必须len-1
{
r++;
if (blocks[r] == 'B')
num++;
if (r - l + 1 == k)
{
ans = min(ans, k - num);
if (blocks[l] == 'B')
{
num--;
}
l++;
}
}
return ans;
}
};
class Solution {
public:
int minimumRecolors(string blocks, int k) {
int l=0,r=-1;
int num=0,ans=INT_MAX,len=blocks.size();
while(r<len-1)//此处必须len-1
{
r++;
if(blocks[r]=='W')
{
num++;
}
if(r-l+1==k)
{
ans=min(ans,num);
if(blocks[l]=='W')
num--;
l++;
}
}
return ans==INT_MAX?0:ans;
}
};
6157. 二进制字符串重新安排顺序需要的时间
难度中等
给你一个二进制字符串 s 。在一秒之中,所有 子字符串 "01" 同时 被替换成 "10" 。这个过程持续进行到没有 "01" 存在。
请你返回完成这个过程所需要的秒数。
示例 1:
输入:s = "0110101"
输出:4
解释:
一秒后,s 变成 "1011010" 。
再过 1 秒后,s 变成 "1101100" 。
第三秒过后,s 变成 "1110100" 。
第四秒后,s 变成 "1111000" 。
此时没有 "01" 存在,整个过程花费 4 秒。
所以我们返回 4 。
示例 2:
输入:s = "11100"
输出:0
解释:
s 中没有 "01" 存在,整个过程花费 0 秒。
所以我们返回 0 。
提示:
1 <= s.length <= 1000s[i]要么是'0',要么是'1'。
暴力模拟
class Solution {
public:
int secondsToRemoveOccurrences(string s) {
int ans=0;
bool flag=true;
while(flag)
{
flag=false;
for(int i=1;i<s.size();i++)
{
if(s[i-1]=='0'&&s[i]=='1')
{
s[i-1]='1';
s[i]='0';
i++;
flag=true;
}
}
if(flag)
ans++;
}
return ans;
}
};
一次遍历(重点)
思考时,我们可以把 01→ 10 的替换看成是 1 向左移动。每一秒,如果 1 的左面是 0,则会向左移动一步。注意连续的 11 不能同时向左移动。
每个 1 向左移动的过程中,有两种情况:
-
在到达最终位置之前,未受到左侧的 1 的 “阻挡”,也就是每一秒都移动了一次,此时,移动次数 = 其左侧 00的个数
-
在到达最终位置之前,受到了左侧 1 的 “阻挡”,也就是说,在某一时刻,其与左侧的 11相邻而组成了 11。在此之后,我们可以证明,当左侧的那个 1到达最终位置时,右侧的 1 一定与左侧的 1 间隔 1 个 0。此时,移动次数 = 左侧 1 的移动次数 + 1。
上面结论的证明: -
当两个 1 相邻时,若左侧的 1 可以移动,此时右侧的 1 不可以移动,因此会有 11→101;
-
当两个 1 间隔 1 个 0 时,也就是 101,那么当左侧的 1可以移动时,右侧的 1 也可以移动,因此 101 继续保持一个 0 的间隔;当左侧的 1不能移动时,右侧的 1 可以移动,此时两者又相邻了,成为 11。
-
综上所述,这两个 1 移动时,其间隔不会超过 1 个 0,但是当左侧的 1 刚到达最终位置时,两者又不可能相邻,因此,两者必定间隔 1 个 0。
class Solution {
public:
int secondsToRemoveOccurrences(string s) {
int res = 0;
for(int i = 0, cnt = 0; i < s.size(); ++i) {
if(s[i] == '0') ++cnt;
else if(cnt > 0) res = max(res + 1, cnt);
}
return res;
}
};
6158. 字母移位 II
难度中等
给你一个小写英文字母组成的字符串 s 和一个二维整数数组 shifts ,其中 shifts[i] = [starti, endi, directioni] 。对于每个 i ,将 s 中从下标 starti 到下标 endi (两者都包含)所有字符都进行移位运算,如果 directioni = 1 将字符向后移位,如果 directioni = 0 将字符向前移位。
将一个字符 向后 移位的意思是将这个字符用字母表中 下一个 字母替换(字母表视为环绕的,所以 'z' 变成 'a')。类似的,将一个字符 向前 移位的意思是将这个字符用字母表中 前一个 字母替换(字母表是环绕的,所以 'a' 变成 'z' )。
请你返回对 s 进行所有移位操作以后得到的最终字符串。
示例 1:
输入:s = "abc", shifts = [[0,1,0],[1,2,1],[0,2,1]]
输出:"ace"
解释:首先,将下标从 0 到 1 的字母向前移位,得到 s = "zac" 。
然后,将下标从 1 到 2 的字母向后移位,得到 s = "zbd" 。
最后,将下标从 0 到 2 的字符向后移位,得到 s = "ace" 。
示例 2:
输入:s = "dztz", shifts = [[0,0,0],[1,1,1]]
输出:"catz"
解释:首先,将下标从 0 到 0 的字母向前移位,得到 s = "cztz" 。
最后,将下标从 1 到 1 的字符向后移位,得到 s = "catz" 。
提示:
1 <= s.length, shifts.length <= 5 * 104shifts[i].length == 30 <= starti <= endi < s.length0 <= directioni <= 1s只包含小写英文字母。
一维差分数组
这个题多次对区间进行加一和减一操作,很容易可以想到使用差分来做,需要注意的细节就是小写字母是循环的
class Solution {
public:
string shiftingLetters(string s, vector<vector<int>>& shifts) {
vector<int>deffer(s.size()+1,0);
vector<int>num(s.size(),0);
for(int i=0;i<shifts.size();i++)
{
int t=shifts[i][2]==1?1:-1;
deffer[shifts[i][0]]+=t;
deffer[shifts[i][1]+1]-=t;
}
num[0]=deffer[0];
for(int i=1;i<s.size();i++)
{
num[i]=num[i-1]+deffer[i];
}
for(int i=0;i<s.size();i++)
{
int x=((s[i]-'a')+num[i])%26;
if(x>=0)
s[i]=x+'a';
else
{
s[i]=x+'z'+1;
}
}
return s;
}
};
线段树
看题解的时候看到一位大佬用线段树写了出来,不过是java版本的,看代码思路还是很清晰的
class Solution {
public String shiftingLetters(String s, int[][] shifts) {
// 首先更新所有区间
// d = 1,则➕1;反之➖1
for (int[] shift : shifts) {
int start = shift[0], end = shift[1], d = shift[2];
update(root, 0, N, start, end, d == 1 ? 1 : -1);
}
char[] cur = s.toCharArray();
for (int i = 0; i < cur.length; i++) {
int c = cur[i] - 'a';
// 查询区间 [i, i] 最终修改值
int d = query(root, 0, N, i, i);
c = ((c + d) % 26 + 26) % 26;
cur[i] = (char) (c + 'a');
}
return new String(cur);
}
// *************** 下面是模版 ***************
class Node {
Node left, right;
int val, add;
}
private int N = (int) 1e9;
private Node root = new Node();
public void update(Node node, int start, int end, int l, int r, int val) {
if (l <= start && end <= r) {
node.val += (end - start + 1) * val;
node.add += val;
return ;
}
int mid = (start + end) >> 1;
pushDown(node, mid - start + 1, end - mid);
if (l <= mid) update(node.left, start, mid, l, r, val);
if (r > mid) update(node.right, mid + 1, end, l, r, val);
pushUp(node);
}
public int query(Node node, int start, int end, int l, int r) {
if (l <= start && end <= r) return node.val;
int mid = (start + end) >> 1, ans = 0;
pushDown(node, mid - start + 1, end - mid);
if (l <= mid) ans += query(node.left, start, mid, l, r);
if (r > mid) ans += query(node.right, mid + 1, end, l, r);
return ans;
}
private void pushUp(Node node) {
node.val = node.left.val + node.right.val;
}
private void pushDown(Node node, int leftNum, int rightNum) {
if (node.left == null) node.left = new Node();
if (node.right == null) node.right = new Node();
if (node.add == 0) return ;
node.left.val += node.add * leftNum;
node.right.val += node.add * rightNum;
// 对区间进行「加减」的更新操作,下推懒惰标记时需要累加起来,不能直接覆盖
node.left.add += node.add;
node.right.add += node.add;
node.add = 0;
}
}
第307场周赛
6152. 赢得比赛需要的最少训练时长
难度简单
你正在参加一场比赛,给你两个 正 整数 initialEnergy 和 initialExperience 分别表示你的初始精力和初始经验。
另给你两个下标从 0 开始的整数数组 energy 和 experience,长度均为 n 。
你将会 依次 对上 n 个对手。第 i 个对手的精力和经验分别用 energy[i] 和 experience[i] 表示。当你对上对手时,需要在经验和精力上都 严格 超过对手才能击败他们,然后在可能的情况下继续对上下一个对手。
击败第 i 个对手会使你的经验 增加 experience[i],但会将你的精力 减少 energy[i] 。
在开始比赛前,你可以训练几个小时。每训练一个小时,你可以选择将增加经验增加 1 或者 将精力增加 1 。
返回击败全部 n 个对手需要训练的 最少 小时数目。
示例 1:
输入:initialEnergy = 5, initialExperience = 3, energy = [1,4,3,2], experience = [2,6,3,1]
输出:8
解释:在 6 小时训练后,你可以将精力提高到 11 ,并且再训练 2 个小时将经验提高到 5 。
按以下顺序与对手比赛:
- 你的精力与经验都超过第 0 个对手,所以获胜。
精力变为:11 - 1 = 10 ,经验变为:5 + 2 = 7 。
- 你的精力与经验都超过第 1 个对手,所以获胜。
精力变为:10 - 4 = 6 ,经验变为:7 + 6 = 13 。
- 你的精力与经验都超过第 2 个对手,所以获胜。
精力变为:6 - 3 = 3 ,经验变为:13 + 3 = 16 。
- 你的精力与经验都超过第 3 个对手,所以获胜。
精力变为:3 - 2 = 1 ,经验变为:16 + 1 = 17 。
在比赛前进行了 8 小时训练,所以返回 8 。
可以证明不存在更小的答案。
示例 2:
输入:initialEnergy = 2, initialExperience = 4, energy = [1], experience = [3]
输出:0
解释:你不需要额外的精力和经验就可以赢得比赛,所以返回 0 。
提示:
n == energy.length == experience.length1 <= n <= 1001 <= initialEnergy, initialExperience, energy[i], experience[i] <= 100
贪心
记 a 为当前精力,b 为当前经验,我们先从 a = initialEnergy 与 b = initialExperience 开始比赛。
- 若 a <= energy[i],那么我们一开始至少需要增加 energy[i] - a + 1 的精力,才能让现在的精力刚好超过对手 i。
- 若 b <= experience[i],那么我们一开始至少需要增加 experience[i] - b + 1 的经验,才能让现在的经验刚好超过对手 i。
因为精力和经验的增加量都取到了至少需要的值,因此我们能得到最优答案。
class Solution {
public:
int minNumberOfHours(int a, int b, vector<int>& energy, vector<int>& experience) {
int sum=0;
for(int i=0;i<energy.size();i++)
sum+=energy[i];
if(sum-a>=0)
{
ans=sum-a+1;// 精力上无法打败对手,因此最开始需要增加一些精力,使得精力恰好满足要求
}
else
ans=0;//不需要补充精力
for(int i=0;i<experience.size();i++)
{
if(b<=experience[i])
{
ans+=experience[i]-b+1;
b=experience[i]+1; // 经验上无法打败对手,因此最开始需要增加一些经验,使得经验恰好满足要求
}
b+=experience[i]; // 打败对手后数值变更
}
return ans;
}
};
6166. 最大回文数字
难度中等
给你一个仅由数字(0 - 9)组成的字符串 num 。
请你找出能够使用 num 中数字形成的 最大回文 整数,并以字符串形式返回。该整数不含 前导零 。
注意:
- 你 无需 使用
num中的所有数字,但你必须使用 至少 一个数字。 - 数字可以重新排序。
示例 1:
输入:num = "444947137"
输出:"7449447"
解释:
从 "444947137" 中选用数字 "4449477",可以形成回文整数 "7449447" 。
可以证明 "7449447" 是能够形成的最大回文整数。
示例 2:
输入:num = "00009"
输出:"9"
解释:
可以证明 "9" 能够形成的最大回文整数。
注意返回的整数不应含前导零。
提示:
1 <= num.length <= 105num由数字(0 - 9)组成
贪心
一个回文串(如 998767899,123321)可以被分成两部分:
两边对应的部分(如 9987 和 7899,123 和 321),这两部分中的数字每种都出现偶数次。
中间单独一个数字(如 6),这部分是可选的。
因此令 cnt[i] 表示数字 i 出现的次数,我们先从 9 到 0 枚举第一部分中出现的数,再看是否还有剩下的数放进中间单独的数字即可。复杂度O(n)。
由于题目要求不能有前导零,所以代码细节部分需要注意
class Solution {
public:
string largestPalindromic(string num) {
string ans1,ans2; // ans1 表示对应的部分中的前一半,ans2 是 ans1 的倒序
int cnt[10]={0};
for(int i=0;i<num.size();i++)
cnt[num[i]-'0']++;//统计字符个数
for(int i=9;i>=0;i--)
{
if(i==0&&ans1.size()==0) // 已经枚举到了 0,但是之前从来没有加入过别的数字。此时加入 0 将会导致前导 0,因此直接结束。
break;
for(int j=0;j<cnt[i]/2;j++) // 在这部分中出现过的数必须出现偶数次
{
ans1+=(i+'0');
}
cnt[i]-=cnt[i]/2*2;
}
ans2=ans1;
reverse(ans2.begin(),ans2.end());
for(int i=9;i>=0;i--)
{
// 此时 0 无需跳过,因为单独一个 0 是合法的答案
if(cnt[i])
{
ans1+=i+'0';
break;
}// 看看是否还有剩下的数,可以作为中间单独的一个数字
}
return ans1+ans2;
}
};
6154. 感染二叉树需要的总时间
难度中等
给你一棵二叉树的根节点 root ,二叉树中节点的值 互不相同 。另给你一个整数 start 。在第 0 分钟,感染 将会从值为 start 的节点开始爆发。
每分钟,如果节点满足以下全部条件,就会被感染:
- 节点此前还没有感染。
- 节点与一个已感染节点相邻。
返回感染整棵树需要的分钟数*。*
示例 1:

输入:root = [1,5,3,null,4,10,6,9,2], start = 3
输出:4
解释:节点按以下过程被感染:
- 第 0 分钟:节点 3
- 第 1 分钟:节点 1、10、6
- 第 2 分钟:节点5
- 第 3 分钟:节点 4
- 第 4 分钟:节点 9 和 2
感染整棵树需要 4 分钟,所以返回 4 。
示例 2:

输入:root = [1], start = 1
输出:0
解释:第 0 分钟,树中唯一一个节点处于感染状态,返回 0 。
提示:
- 树中节点的数目在范围
[1, 105]内 1 <= Node.val <= 105- 每个节点的值 互不相同
- 树中必定存在值为
start的节点
第一次做这种类型的题,没做出来,但是看题解的时候受益匪浅,为了保证能够深刻理解这题,打算等次日在把这题所有可以实现的方法全部写一遍,所以此处有些代码不是自己手写的
建图求深度
实际上就是给定一棵无根树,求以 start 为根时树的深度。只不过无根树是以二叉树的形式给出的,因此需要先从二叉树中建出无向图。最后求出深度即可,求深度可用bfs也可用dfs
dfs建图+dfs求深度
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
int mx = 0;
vector<vector<int>> e;
void dfs1(TreeNode *node) {
if (node == nullptr) return;
mx = max(mx, node->val);
dfs1(node->left); dfs1(node->right);
}
void dfs2(TreeNode *node) {
if (node->left != nullptr) {
e[node->val].push_back(node->left->val);
e[node->left->val].push_back(node->val);
dfs2(node->left);
}
if (node->right != nullptr) {
e[node->val].push_back(node->right->val);
e[node->right->val].push_back(node->val);
dfs2(node->right);
}
}
int dfs3(int sn, int fa) {
int ret = 0;
for (int fn : e[sn]) if (fn != fa) ret = max(ret, dfs3(fn, sn) + 1);
return ret;
}
public:
int amountOfTime(TreeNode* root, int start) {
dfs1(root);
e.resize(mx + 1);
dfs2(root);
return dfs3(start, -1);
}
};
dfs建图+bfs求深度
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
map<int,vector<int>> mp;
int vis[100001];
public:
void dfs(TreeNode *root){
if(root->left){
mp[root->val].push_back(root->left->val);
mp[root->left->val].push_back(root->val);
dfs(root->left);
}
if(root->right){
mp[root->val].push_back(root->right->val);
mp[root->right->val].push_back(root->val);
dfs(root->right);
}
}
int amountOfTime(TreeNode* root, int start) {
int time=-1;
dfs(root);
if(mp.size()==0) return 0;
queue<int> q;
q.push(start);
while(!q.empty()){
int n=q.size();
while(n--){
int fr=q.front();
vis[fr]=1;
q.pop();
for(auto v:mp[fr]){
if(!vis[v]) {
q.push(v);
}
}
}
time++;
}
return time;
}
};
建立带父母信息的二叉树
让每个结点关联起他的父结点,类似于线索树
这样广度搜索,每次把他的孩子结点和父结点都感染,一轮时间+1
思路虽然不一样但是代码实质和建立无向图差不多
Java版本:
class Solution {
public int amountOfTime(TreeNode root, int start) {
//关联父节点 <当前结点,当前结点父节点>
Map<TreeNode,TreeNode> map = new HashMap<>();
//关联是否被感染 <当前结点,是否感染>
Map<TreeNode,Boolean> flag = new HashMap<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
map.put(root,null); //根结点无父节点
//指向感染结点
TreeNode x = null;
while(!queue.isEmpty()){
int size = queue.size();
//层序遍历,确定好每个结点的 父结点 及 是否被感染信息
while(size!=0){
TreeNode t = queue.poll();
size--;
//找到感染结点
if(t.val == start){
x = t;
flag.put(t,true);
}
else
flag.put(t,false);
if(t.left!=null){
queue.add(t.left);
map.put(t.left,t);
}
if(t.right!=null){
queue.add(t.right);
map.put(t.right,t);
}
}
}
//感染结点为首,入队
queue.add(x);
int minutes = 0;
while(!queue.isEmpty()){
int size = queue.size();
while(size!=0){
TreeNode t = queue.poll();
size--;
if(t.left!=null && flag.get(t.left)==false){
queue.add(t.left);
flag.put(t.left,true);
}
if(t.right!=null && flag.get(t.right)==false){
queue.add(t.right);
flag.put(t.right,true);
}
//就层序遍历多扫一个父亲结点
if(map.get(t)!=null && flag.get(map.get(t))==false){
//父节点入队
queue.add(map.get(t));
flag.put(map.get(t),true);
}
}
//每轮结束 minutes++
minutes++;
}
return minutes-1;
}
}
一次dfs传递感染时间
一个节点被感染,有四个可能,从上一个节点被感染,本身被感染,左子树传染的感染,右子树传染的感染
可以在dfs里面传递一个参数sum,如果是-1代表之前没有被感染,不是-1则代表感染的时间
并把sum+1作为返回值(没感染返回-1),代表被本节点感染的时间
然后如果没有感染就判断一下,本节点是否是感染节点,是就设置sum为0
如果感染了,就把sum+1作为参数传递到左右子树中
如果还未感染,则感染会从左子树或右子树来,选择被感染的子树的返回值,作为本节点感染的时间,并传递到另一个子树当中
最后,最大的sum值即为返回的结果
class Solution {
public:
int max=0;
int dfs(TreeNode* root, int start,int sum){
if(root==NULL)return -1;
if(sum==-1&&root->val==start){//本节点为感染节点
sum=0;
}
if(sum!=-1){//本节点已感染,传递到子树中
dfs(root->left,start,sum+1);
dfs(root->right,start,sum+1);
if(sum>max)max=sum;
return sum+1;
}else{
int tem=dfs(root->left,start,sum);
if(tem!=-1){//左子树被感染,传递到右子树
sum=tem;
dfs(root->right,start,sum+1);
}else{//右子树被感染,传递到左子树
sum=dfs(root->right,start,sum);
dfs(root->left,start,sum+1);
}
}
if(sum>max)max=sum;
if(sum!=-1)return sum+1;
return -1;
}
int amountOfTime(TreeNode* root, int start) {
dfs(root,start,-1);
return max;
}
};
不建图直接求深度
这道题目问题可以转换为,以start结点为起点的最长路径
那么,如何求解呢?
首先,我们将其分为两部分
1.以start结点向下的路径
2.以start结点向上的路径
- 比较容易求解,我们可以吧start结点看作root结点,然后求其树的高度,即为向下路径中最长的那一条
- 如何求解?重点来了:
这里我们需要对二叉树的数据结构,后序遍历,dfs调用过程(即压栈弹栈过程)有一个非常透彻的理解,才能理解该算法的巧妙之处
既然start结点为起点,那么可以理解为向上弹栈过程中,每弹一层栈,距离+1,注意,这一规则仅适用于,所有压栈的结点,(这句话换个意思就是说,从root结点我们是如何通过进行dfs压栈来得到start结点,那么我们对其所有被压结点进行弹栈过程中距离+1),而我们需要求以start结点为起点的最长路径
所有弹栈结点,即为中间结点mid,假设其到start结点距离为d,而另一子树高度为h,那么以start结点为起点,并且经过中间结点mid的最长距离为d + h
同时,我们维护最长路径变量,不断更新
那么,我们前面提到了要对二叉树的数据结构,后序遍历,dfs调用过程(即压栈弹栈过程)有一个非常透彻的理解
后序遍历如何体现?
这里使用后序遍历的原因是,我们在对mid结点处理时,需要首先得到左右子树的返回值,距离d或者高度h,所以使用后序遍历,即先访问左右子树,最后访问根节点
class Solution {
public:
int dfs(TreeNode *root, const int start, bool &flag) {
if (root == nullptr) {
return -1;//自底向上计算高度
}
bool lflag = false, rflag =false;//通过bool变量来确定压栈弹栈路径
int l = dfs(root->left, start, lflag) + 1;
int r = dfs(root->right, start, rflag) + 1;
if (lflag || rflag) {//当前结点为mid结点
res = max(res, l + r);//更新最长路径
flag = true;//向上传递弹栈路径
if (lflag) {//确定start结点到当前结点距离
return l;
}
else {
return r;
}
}
if (root->val == start) {
res = max(l, r);//计算所有向下路径中最大值,即部分1
flag = true;
return 0;//弹栈过程重新计算距离,start为起点所以距离为0
}
return max(l, r);//正常计算树的高度,贪心选择最长路径
}
int res = 0;
int amountOfTime(TreeNode* root, int start) {
bool flag = false;
int level = dfs(root, start, flag);
return res;
}
};

这篇博客涵盖了多个算法问题,包括字符串操作和树结构的处理。首先,介绍了如何在给定字符串中用最少的涂色次数得到连续的黑色块,接着是二进制字符串重新排列顺序所需的时间。接着讨论了如何在字母表中移动字母以及计算赢得比赛所需的最少训练时长。最后,探讨了感染二叉树所需的时间,通过多种方法解决,如建立无向图、广度优先搜索和深度优先搜索。这些问题都需要巧妙的算法思维和数据结构应用。
1391

被折叠的 条评论
为什么被折叠?



