Gear with Code Java Engineer

LeetCode周赛179

2020-03-08

LeetCode第179次周赛题。

1 生成每种字符都是奇数个的字符串

  1. 题目描述

    给你一个整数 n,请你返回一个含 n 个字符的字符串,其中每种字符在该字符串中都恰好出现 奇数次 。

    返回的字符串必须只含小写英文字母。如果存在多个满足题目要求的字符串,则返回其中任意一个即可。

    示例 1:

    输入:n = 4 输出:”pppz” 解释:”pppz” 是一个满足题目要求的字符串,因为 ‘p’ 出现 3 次,且 ‘z’ 出现 1 次。当然,还有很多其他字符串也满足题目要求,比如:”ohhh” 和 “love”。

  2. 思路

    如果n是奇数就生成n个’a’,否则,生成1个’a’和n-1个’b’。

  3. C++代码

    class Solution {
    public:
        string generateTheString(int n) {
            if (n == 0) {
                return string();
            }
               
            if (n % 2 == 1) {
                return string(n, 'a');
            } else {
                return string(1, 'a') + string(n - 1, 'b');
            }
        }
    };
    

2 灯泡开关 III

  1. 题目描述

    房间中有 n 枚灯泡,编号从 1 到 n,自左向右排成一排。最初,所有的灯都是关着的。

    在 k 时刻( k 的取值范围是 0 到 n - 1),我们打开 light[k] 这个灯。

    灯的颜色要想 变成蓝色 就必须同时满足下面两个条件:

    灯处于打开状态。 排在它之前(左侧)的所有灯也都处于打开状态。 请返回能够让 所有开着的 灯都 变成蓝色 的时刻 数目 。

    示例1:

    img

    输入:light = [2,1,3,5,4] 输出:3 解释:所有开着的灯都变蓝的时刻分别是 1,2 和 4 。

  2. 思路分析

    • 用一个新的数组记录每个灯的颜色,0不亮,1黄色,2蓝色。
    • 按照电灯顺序依次电灯。
    • 若点的灯是第一个灯,直接点为蓝色。
    • 若点的灯不是第一个灯,判断它前面灯的颜色。若是蓝色,则此灯点为蓝色。若不是,则此灯点为黄色。
    • 每点亮一个蓝色的灯,判断它右边的灯是否是黄色,如果是,则点为蓝色。
    • 记录点亮的所有的灯中最大的编号。每次点灯时,若点亮的最后一个灯是蓝色,判断此灯是否是最大编号,若是,结果+1;
  3. java代码

    class Solution {
        public int numTimesAllBlue(int[] light) {
            if (light == null || light.length == 0) {
                return 0;
            }
            int length = light.length;
            int[] color = new int[length];
            int result = 0;
            int maxLight = 0;
            for (int i = 0; i < length; ++i) {
                // 如果打开的是第0个灯
                if (light[i] - 1 == 0) {
                    // 直接变成蓝色
                    color[0] = 2;
                    // 如果这是亮着的最右边的灯
                    if (0 == maxLight) {
                        ++result;
                    } else {
                        // 将它右边的连续的亮着的灯都变成蓝色
                        int j = 1;
                        for (; j <= maxLight; ++j) {
                            if (color[j] == 1) {
                                color[j] = 2;
                            } else {
                                break;
                            }
                        }
                        if (j == maxLight + 1) {
                            ++result;
                        }
                    }
                } else {
                    // 如果当前灯前面的灯是蓝色,则变为蓝色
                    if (color[light[i] - 2] == 2) {
                        color[light[i] - 1] = 2;
                        // 如果此灯是最右边的灯
                        if (light[i] - 1 >= maxLight) {
                            ++result;
                            maxLight= light[i] - 1;
                        } else {
                            // 将右边亮着的连续的灯变为蓝色
                            int j = light[i];
                            for (; j <= maxLight; ++j) {
                                if (color[j] == 1) {
                                    color[j] = 2;
                                } else {
                                    break;
                                }
                            }
                            if (j == maxLight + 1) {
                                ++result;
                            }
                        }
                    } else {
                        // 前面的灯不是蓝色,点亮此灯
                        color[light[i] - 1] = 1;
                        maxLight = maxLight > light[i] - 1 ? maxLight : light[i] - 1;
                    }
                }
                   
            }
               
            return result;
        }
    }
    

3 通知所有员工所需的时间

  1. 题目描述

    公司里有 n 名员工,每个员工的 ID 都是独一无二的,编号从 0 到 n - 1。公司的总负责人通过 headID 进行标识。

    在 manager 数组中,每个员工都有一个直属负责人,其中 manager[i] 是第 i 名员工的直属负责人。对于总负责人,manager[headID] = -1。题目保证从属关系可以用树结构显示。

    公司总负责人想要向公司所有员工通告一条紧急消息。他将会首先通知他的直属下属们,然后由这些下属通知他们的下属,直到所有的员工都得知这条紧急消息。

    第 i 名员工需要 informTime[i] 分钟来通知它的所有直属下属(也就是说在 informTime[i] 分钟后,他的所有直属下属都可以开始传播这一消息)。

    返回通知所有员工这一紧急消息所需要的 分钟数 。

    示例:

    img

    输入:n = 6, headID = 2, manager = [2,2,-1,2,2,2], informTime = [0,0,1,0,0,0] 输出:1 解释:id = 2 的员工是公司的总负责人,也是其他所有员工的直属负责人,他需要 1 分钟来通知所有员工。 上图显示了公司员工的树结构。

  2. 思路分析

    • 将输入恢复成树结构,每个节点的值就是它传递消息所需的时间。
    • 那么问题转化为求和最大的路径。
    • DFS。
  3. java代码

    class Solution {
        public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) {
            // 首先用一个map把树恢复出来,key是员工id,value是他的从属id
            ArrayList<Integer>[] map = new ArrayList[n];
               
            // 遍历manager数组,将每个下属放到他的领导的map中
            for (int i = 0; i < n; ++i) {
                if (manager[i] == -1) {
                    continue;
                }
                if (map[manager[i]]  == null) {
                    map[manager[i]] = new ArrayList<Integer>();
                }
                map[manager[i]].add(i);
            }
               
            // 从领导开始,分发消息
            // 使用深度优先遍历,判断到每一个最后的员工手上最长需要多久
            return maxTime(headID, map, informTime);
        }
           
        public int maxTime(int header, ArrayList<Integer>[] map, int[] informTime) {
            if (map[header]== null) {
                return 0;
            }
               
            ArrayList<Integer> employees = (ArrayList<Integer>)map[header];
            int result = 0;
            for (int id : employees) {
                int temp = maxTime(id, map, informTime);
                result = result > temp ? result : temp;
            }
               
            return result + informTime[header];
        }
    }
    

4 T 秒后青蛙的位置

  1. 题目描述

    给你一棵由 n 个顶点组成的无向树,顶点编号从 1 到 n。青蛙从 顶点 1 开始起跳。规则如下:

    在一秒内,青蛙从它所在的当前顶点跳到另一个 未访问 过的顶点(如果它们直接相连)。 青蛙无法跳回已经访问过的顶点。 如果青蛙可以跳到多个不同顶点,那么它跳到其中任意一个顶点上的机率都相同。 如果青蛙不能跳到任何未访问过的顶点上,那么它每次跳跃都会停留在原地。 无向树的边用数组 edges 描述,其中 edges[i] = [fromi, toi] 意味着存在一条直接连通 fromi 和 toi 两个顶点的边。

    返回青蛙在 t 秒后位于目标顶点 target 上的概率。

    示例:

    img

    输入:n = 7, edges = [[1,2],[1,3],[1,7],[2,4],[2,6],[3,5]], t = 2, target = 4 输出:0.16666666666666666 解释:上图显示了青蛙的跳跃路径。青蛙从顶点 1 起跳,第 1 秒 有 1/3 的概率跳到顶点 2 ,然后第 2 秒 有 1/2 的概率跳到顶点 4,因此青蛙在 2 秒后位于顶点 4 的概率是 1/3 * 1/2 = 1/6 = 0.16666666666666666 。

  2. 思路分析

    • 先将输入恢复成树结构。
    • 使用深度优先遍历寻找目标节点,并计算到达目标节点的概率。
    • 值得注意的是,题目要求的是t秒后位于目标顶点上的概率,那么在找到目标节点时,若目标节点不是叶子节点,且t不为0的话,还要继续向下跳,则概率为0。若目标节点是叶子节点,则返回到达此节点的概率。
  3. java代码

    class Solution {
        public double frogPosition(int n, int[][] edges, int t, int target) {
            if (n == 1) {
                if (target == 1) {
                    return 1.0d;
                } else {
                    return 0.0d;
                } 
            }
            // 还是先把树恢复出来,然后深度优先遍历
            ArrayList<Integer>[] tree = new ArrayList[n + 1]; // 为了方便空一个0节点
            for (int[] edge : edges) {
                if (tree[edge[0]] == null) {
                    tree[edge[0]] = new ArrayList<Integer>();
                    tree[edge[0]].add(0); // 用第0个数作为标记位,记录这个节点是否被访问过。
                }
                if (tree[edge[1]] == null) {
                    tree[edge[1]] = new ArrayList<Integer>();
                    tree[edge[1]].add(0);
                }
                   
                tree[edge[0]].add(edge[1]);
                tree[edge[1]].add(edge[0]);
            }
               
               
            return dfs(1, tree, target, t, 1.0d);
        }
           
        public double dfs(int root, ArrayList<Integer>[] tree, int target, int t, double p) {
            // 如果没有时间了,直接返回
            if (t < 0) {
                return 0.0d;
            }
            // 如果这个节点已经来过了
            if (tree[root].get(0) == 1) {
                return 0.0d;
            }
               
            // 如果这个节点就是目标节点
            if (root == target) {
                // 如果此节点就是叶子节点,那么直接返回p
                if (root != 1 && tree[root].size() == 2) {
                    return p;
                } else {
                    // 如果此节点有子节点,如果时间恰好是0,返回p
                    if (t == 0) {
                        return p;
                    } else {
                        // 否则,就会往下跳,并且无法返回,返回0
                        return 0.0d;
                    }
       
                }
            }
               
            // 如果这个节点是叶子节点返回0
            if (root != 1 && tree[root].size() == 2) {
                return 0.0d;
            }
               
            // 标记这个节点已经来过
            tree[root].set(0, 1);
               
            // 计算到达每个子节点的概率
            int subNodeNum;
            // 计算子节点的数量
            // 如果是根节点,那么所有与它相连的节点都是他的子节点
            if (root == 1) {
                subNodeNum = tree[root].size() - 1; // -1 去掉头部的标记节点
            } else {
                // 如果不是根节点,那么与他相连的节点中有一个是它的父亲节点
                subNodeNum = tree[root].size() - 2;
            }
            double averageP = (1.0d / (double)subNodeNum) * p;
            // 遍历这个节点的所有子节点,如果有一个节点可以跳到target,返回他们跳到target的概率
            for (int i = 1; i < tree[root].size(); ++i) {
                double subP = dfs(tree[root].get(i), tree, target, t - 1, averageP);
                if (Math.abs(subP - 0.0d) < 0.0000001) {
                    continue;
                } else {
                    return subP;
                }
            }
               
            // 退出此节点
            tree[root].set(0, 0);
               
            return 0.0d;
        }
    }
    

Similar Posts

上一篇 LeetCode周赛178

Comments