LeetCode Weekly Contest 172

5315. 6 和 9 组成的最大数字

给你一个仅由数字 6 和 9 组成的正整数 num。

你最多只能翻转一位数字,将 6 变成 9,或者把 9 变成 6 。

请返回你可以得到的最大数字。

示例 1:

输入:num = 9669
输出:9969
解释:
改变第一位数字可以得到 6669 。
改变第二位数字可以得到 9969 。
改变第三位数字可以得到 9699 。
改变第四位数字可以得到 9666 。
其中最大的数字是 9969 。
示例 2:

输入:num = 9996
输出:9999
解释:将最后一位从 6 变到 9,其结果 9999 是最大的数。
示例 3:

输入:num = 9999
输出:9999
解释:无需改变就已经是最大的数字了。

代码

class Solution {
     
    public int maximum69Number (int num) {
     
        String snum = String.valueOf(num);
        int slen = snum.length(), i = 0;
        for (i=0; i <slen; ++i) {
     
            if (snum.charAt(i) == '6') {
     
                return Integer.parseInt(snum.substring(0, i) + '9' + snum.substring(i+1));
            }
        }
        return num;
    }
};

5316. 竖直打印单词

给你一个字符串 s。请你按照单词在 s 中的出现顺序将它们全部竖直返回。
单词应该以字符串列表的形式返回,必要时用空格补位,但输出尾部的空格需要删除(不允许尾随空格)。
每个单词只能放在一列上,每一列中也只能有一个单词。

示例 1:

输入:s = “HOW ARE YOU”
输出:[“HAY”,“ORO”,“WEU”]
解释:每个单词都应该竖直打印。
“HAY”
“ORO”
“WEU”
示例 2:

输入:s = “TO BE OR NOT TO BE”
输出:[“TBONTB”,“OEROOE”," T"]
解释:题目允许使用空格补位,但不允许输出末尾出现空格。
“TBONTB”
“OEROOE”
" T"
示例 3:

输入:s = “CONTEST IS COMING”
输出:[“CIC”,“OSO”,“N M”,“T I”,“E N”,“S G”,“T”]

提示:

1 <= s.length <= 200
s 仅含大写英文字母。
题目数据保证两个单词之间只有一个空格。

代码

class Solution {
     
    public List<String> printVertically(String s) {
     
        s = s.trim();
        ArrayList<String> ans = new ArrayList<>();
        String[] words = s.split(" ");
        int n = words.length, i = 0, lmax = 0, j = 0;
        for (String word: words) {
     
            lmax = Math.max(lmax, word.length());
        }
        char[] chs = new char[n];
        for (j=0; j<lmax; ++j) {
     
            Arrays.fill(chs, '0');
            for (i=0; i<n; ++i) {
     
                if (j < words[i].length()) {
     
                    chs[i] = words[i].charAt(j);
                } else {
     
                    chs[i] = ' ';
                }
            }
            String tmp = new String(chs);
            tmp = 'a' + tmp;
            tmp = tmp.trim();
            tmp = tmp.substring(1);
            ans.add(tmp);
        }
        return ans;
    }
}

5317. 删除给定值的叶子节点

给你一棵以 root 为根的二叉树和一个整数 target ,请你删除所有值为 target 的 叶子节点 。

注意,一旦删除值为 target 的叶子节点,它的父节点就可能变成叶子节点;如果新叶子节点的值恰好也是 target ,那么这个节点也应该被删除。

也就是说,你需要重复此过程直到不能继续删除。

示例 1:

输入:root = [1,2,3,2,null,2,4], target = 2
输出:[1,null,3,null,4]
解释:
上面左边的图中,绿色节点为叶子节点,且它们的值与 target 相同(同为 2 ),它们会被删除,得到中间的图。
有一个新的节点变成了叶子节点且它的值与 target 相同,所以将再次进行删除,从而得到最右边的图。
示例 2:

输入:root = [1,3,3,3,2], target = 3
输出:[1,3,null,null,2]
示例 3:

输入:root = [1,2,null,2,null,2], target = 2
输出:[1]
解释:每一步都删除一个绿色的叶子节点(值为 2)。
示例 4:

输入:root = [1,1,1], target = 1
输出:[]
示例 5:

输入:root = [1,2,3], target = 1
输出:[1,2,3]

提示:

1 <= target <= 1000
每一棵树最多有 3000 个节点。
每一个节点值的范围是 [1, 1000] 。

思路

递归。先处理左右子树,返回左右子树根节点,根据更新的左右子节点判断当前根节点是否需要删除。

代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
     
    public TreeNode removeLeafNodes(TreeNode root, int target) {
     
        if (root == null) {
     
            return null;
        }
        root.left = removeLeafNodes(root.left, target);
        root.right = removeLeafNodes(root.right, target);
        if (root.left == null && root.right == null) {
     
            return root.val == target? null: root;
        }
        return root;
    }
}

5318. 灌溉花园的最少水龙头数目 显示英文描述

在 x 轴上有一个一维的花园。花园长度为 n,从点 0 开始,到点 n 结束。

花园里总共有 n + 1 个水龙头,分别位于 [0, 1, …, n] 。

给你一个整数 n 和一个长度为 n + 1 的整数数组 ranges ,其中 ranges[i] (下标从 0 开始)表示:如果打开点 i 处的水龙头,可以灌溉的区域为 [i - ranges[i], i + ranges[i]] 。

请你返回可以灌溉整个花园的 最少水龙头数目 。如果花园始终存在无法灌溉到的地方,请你返回 -1 。

示例 1:

输入:n = 5, ranges = [3,4,1,1,0,0]
输出:1
解释:
点 0 处的水龙头可以灌溉区间 [-3,3]
点 1 处的水龙头可以灌溉区间 [-3,5]
点 2 处的水龙头可以灌溉区间 [1,3]
点 3 处的水龙头可以灌溉区间 [2,4]
点 4 处的水龙头可以灌溉区间 [4,4]
点 5 处的水龙头可以灌溉区间 [5,5]
只需要打开点 1 处的水龙头即可灌溉整个花园 [0,5] 。
示例 2:

输入:n = 3, ranges = [0,0,0,0]
输出:-1
解释:即使打开所有水龙头,你也无法灌溉整个花园。
示例 3:

输入:n = 7, ranges = [1,2,1,0,2,1,0,1]
输出:3
示例 4:

输入:n = 8, ranges = [4,0,0,0,0,0,0,0,4]
输出:2
示例 5:

输入:n = 8, ranges = [4,0,0,0,4,0,0,0,4]
输出:1

提示:

1 <= n <= 10^4
ranges.length == n + 1
0 <= ranges[i] <= 100

思路

区间覆盖问题。将线段按照左端点从小到大排序,维护两个变量curLencurIdx,分别表示当前所选区间集合最右端覆盖的位置和当前所选的线段的Id. 每次从curIdx开始,在有序的线段数组中二分查找最后一个左端点位于curLen左侧的线段tmpIdx,在(curIdx, tmpIdx]区间中寻找右端点最右的线段的Id作为新的curIdx, 该线段的右端点位置作为新的curLen. 如此循环直到区间完全覆盖,记录所用的线段个数返回。
注意rightmostNoLargerThan的二分查找的写法,主要关注while循环条件l <= r和返回值return r.

代码

class Solution {
     
    private class Line implements Comparable<Line> {
     
        public int left, right;
        
        public Line(int left, int right) {
     
            this.left = left;
            this.right = right;
        }
        
        @Override
        public int compareTo(Line other) {
     
            return left - other.left;
        }
    }
    
    private int rightmostNoLargerThan(Line[] lines, int beginIdx, int bound) {
     
        int n = lines.length, l = Math.max(beginIdx, 0), r = n-1, m = 0;
        while (l <= r) {
     
            m = (l + r) / 2;
            if (lines[m].left <= bound) {
     
                l = m + 1;
            } else {
     
                r = m - 1;
            }
        }
        return r;
    }
    
    public int minTaps(int n, int[] ranges) {
     
        Line[] lines = new Line[n+1];
        int i = 0;
        for (i=0; i<=n; ++i) {
     
            lines[i] = new Line(i - ranges[i], i + ranges[i]);
        }
        Arrays.sort(lines);
        int curLen = 0, curIdx = -1, cnt = 0, preLen = 0;
        while (curLen < n) {
     
            int tmpIdx = rightmostNoLargerThan(lines, curIdx, curLen);
            preLen = curLen;
            for (i=curIdx + 1; i<=tmpIdx; ++i) {
     
                if (lines[i].right > curLen) {
     
                    curLen = lines[i].right;
                    curIdx = tmpIdx;
                }
            }
            if (preLen == curLen) {
     
                return -1;
            }
            ++cnt;
        }
        return cnt;
    }
}

你可能感兴趣的:(LeetCode,LeetCode,周赛,Java,区间覆盖)