letcode第85场双周赛&第307场周赛(Easy+Medium)

letcode第85场双周赛&第307场周赛(Easy+Medium)

第85场双周赛

6156. 得到 K 个黑块的最少涂色次数

难度简单

给你一个长度为 n 下标从 0 开始的字符串 blocksblocks[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.length
  • 1 <= n <= 100
  • blocks[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 <= 1000
  • s[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 * 104
  • shifts[i].length == 3
  • 0 <= starti <= endi < s.length
  • 0 <= directioni <= 1
  • s 只包含小写英文字母。
一维差分数组

这个题多次对区间进行加一和减一操作,很容易可以想到使用差分来做,需要注意的细节就是小写字母是循环的

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. 赢得比赛需要的最少训练时长

难度简单

你正在参加一场比赛,给你两个 整数 initialEnergyinitialExperience 分别表示你的初始精力和初始经验。

另给你两个下标从 0 开始的整数数组 energyexperience,长度均为 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.length
  • 1 <= n <= 100
  • 1 <= 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 <= 105
  • num 由数字(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:

img

输入: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:

img

输入: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结点向上的路径

  1. 比较容易求解,我们可以吧start结点看作root结点,然后求其树的高度,即为向下路径中最长的那一条
  2. 如何求解?重点来了:
    这里我们需要对二叉树的数据结构,后序遍历,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;
    }
};
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值