第 283 场周赛 总结
1. Excel 表中某个范围内的单元格
描述:
Excel 表中的一个单元格 (r, c)
会以字符串 "<col><row>"
的形式进行表示,其中:
-
<col>
即单元格的列号c
。用英文字母表中的字母 标识。- 例如,第
1
列用'A'
表示,第2
列用'B'
表示,第3
列用'C'
表示,以此类推。
- 例如,第
-
<row>
即单元格的行号r
。第r
行就用 整数r
标识。
给你一个格式为 "<col1><row1>:<col2><row2>"
的字符串 s
,其中 <col1>
表示 c1
列,<row1>
表示 r1
行,<col2>
表示 c2
列,<row2>
表示 r2
行,并满足 r1 <= r2
且 c1 <= c2
。
找出所有满足 r1 <= x <= r2
且 c1 <= y <= c2
的单元格,并以列表形式返回。单元格应该按前面描述的格式用 字符串 表示,并以 非递减 顺序排列(先按列排,再按行排)。
示例 1:
输入:s = "K1:L2"
输出:["K1","K2","L1","L2"]
解释:
上图显示了列表中应该出现的单元格。
红色箭头指示单元格的出现顺序。
示例 2:
输入:s = "A1:F1"
输出:["A1","B1","C1","D1","E1","F1"]
解释:
上图显示了列表中应该出现的单元格。
红色箭头指示单元格的出现顺序。
提示:
s.length == 5
'A' <= s[0] <= s[3] <= 'Z'
'1' <= s[1] <= s[4] <= '9'
s
由大写英文字母、数字、和':'
组成
// 本人解法 : 找到左上角及右下角对应的 行值 列值
class Solution {
public List<String> cellsInRange(String s) {
List<String> ans = new ArrayList<>();
String[] grids = s.split(":");
// 左上角
int i1 = grids[0].charAt(0) - 'A';
int j1 = grids[0].charAt(1) - '0';
// 右下角
int i2 = grids[1].charAt(0) - 'A';
int j2 = grids[1].charAt(1) - '0';
// 从左上角到右下角依次遍历添加;
for (int i = i1; i <= i2; i++) {
StringBuffer cur = new StringBuffer();
cur.append((char)(i + 'A'));
for (int j = j1; j <= j2; j++) {
cur.append((char)(j + '0'));
ans.add(cur.toString());
cur.deleteCharAt(1);
}
}
return ans;
}
}
// 力扣大神 @arignote(https://leetcode-cn.com/u/arignote/) 解法
// 简洁!!!
class Solution {
public List<String> cellsInRange(String s) {
ArrayList<String> list = new ArrayList<>();
for (char i = s.charAt(0); i <= s.charAt(3); i++) {
for (char j = s.charAt(1); j <= s.charAt(4); j++) {
list.add("" + i + j);
}
}
return list;
}
}
小结: 代码不够简洁!
2. 向数组中追加 K 个整数
描述:
给你一个整数数组 nums
和一个整数 k
。请你向 nums
中追加 k
个 未 出现在 nums
中的、互不相同 的 正 整数,并使结果数组的元素和 最小 。返回追加到 nums
中的 k
个整数之和。
示例 1:
输入:nums = [1,4,25,10,25], k = 2
输出:5
解释:在该解法中,向数组中追加的两个互不相同且未出现的正整数是 2 和 3 。
nums 最终元素和为 1 + 4 + 25 + 10 + 25 + 2 + 3 = 70 ,这是所有情况中的最小值。
所以追加到数组中的两个整数之和是 2 + 3 = 5 ,所以返回 5 。
示例 2:
输入:nums = [5,6], k = 6
输出:25
解释:在该解法中,向数组中追加的两个互不相同且未出现的正整数是 1 、2 、3 、4 、7 和 8 。
nums 最终元素和为 5 + 6 + 1 + 2 + 3 + 4 + 7 + 8 = 36 ,这是所有情况中的最小值。
所以追加到数组中的两个整数之和是 1 + 2 + 3 + 4 + 7 + 8 = 25 ,所以返回 25 。
提示:
1 <= nums.length <= 105
1 <= nums[i], k <= 109
// 本人解法(超时)
class Solution {
public long minimalKSum(int[] nums, int k) {
// 先排序
Arrays.sort(nums);
long ans = 0;
// j代表已经添加的个数,n代表nums的索引
int j = 0, n = 0;
// 一个一个加 (费时)
for (long m = 1; m < 100000000000001L; m++) {
if (j == k) break;
if (n < nums.length && (long)nums[n] == m) {
while (n < nums.length && (long)nums[n] == m) n++;
} else {
ans += m;
j++;
}
}
return ans;
}
}
// 力扣大神 @arignote(https://leetcode-cn.com/u/arignote/) 解法
class Solution {
public long minimalKSum(int[] nums, int k) {
// 先排序
Arrays.sort(nums);
// 利用等差数列公式预先计算 1 + 2 + ... + k的值
long ans = k * (k + 1l) / 2;
// 遍历数组,遇到比k小的,就从ans中减去该值,在加一个比k大的值!
for (int i = 0; i < nums.length; i++) {
if ((i== 0 || nums[i] != nums[i - 1]) && nums[i] <= k) {
ans += ++k;
ans -= nums[i];
}
}
return ans;
}
}
小结: 蛮力不能解决一切,要动动脑子!
3. 根据描述创建二叉树
描述:
给你一个二维整数数组 descriptions
,其中 descriptions[i] = [parenti, childi, isLefti]
表示 parenti
是 childi
在 二叉树 中的 父节点,二叉树中各节点的值 互不相同 。此外:
- 如果
isLefti == 1
,那么childi
就是parenti
的左子节点。 - 如果
isLefti == 0
,那么childi
就是parenti
的右子节点。
请你根据 descriptions
的描述来构造二叉树并返回其 根节点 。测试用例会保证可以构造出 有效 的二叉树。
示例 1:
输入:descriptions = [[20,15,1],[20,17,0],[50,20,1],[50,80,0],[80,19,1]]
输出:[50,20,80,15,17,19]
解释:根节点是值为 50 的节点,因为它没有父节点。
结果二叉树如上图所示。
示例 2:
输入:descriptions = [[1,2,1],[2,3,0],[3,4,1]]
输出:[1,2,null,null,3,4]
解释:根节点是值为 1 的节点,因为它没有父节点。
结果二叉树如上图所示。
提示:
1 <= descriptions.length <= 104
descriptions[i].length == 3
1 <= parenti, childi <= 105
0 <= isLefti <= 1
descriptions
所描述的二叉树是一棵有效二叉树
// 本人解法: 根据题意很好挂每个节点,但是具体返回哪个结点困惑了我很久。
// 最后选择用一个map表,记录每个节点的父节点,如果最后某个节点的父节点是自己,则说明它是头结点!
class Solution {
public TreeNode createBinaryTree(int[][] descriptions) {
// map1 存储不同的 节点
Map<Integer, TreeNode> map1 = new HashMap<>();
// parent 存储 key的父节点value
Map<Integer, Integer> parent = new HashMap<>();
// 建立不同的结点;初始化parent就是自己
for (int i = 0; i < descriptions.length; i++) {
TreeNode cur = new TreeNode(descriptions[i][0]);
TreeNode child = new TreeNode(descriptions[i][1]);
if (!map1.containsKey(cur)) {
map1.put(descriptions[i][0], cur);
parent.put(descriptions[i][0], descriptions[i][0]);
}
if (!map1.containsKey(child)) {
map1.put(descriptions[i][1], child);
parent.put(descriptions[i][1], descriptions[i][1]);
}
}
// 挂节点
for (int i = 0; i < descriptions.length; i++) {
if (descriptions[i][2] == 1) map1.get(descriptions[i][0]).left = map1.get(descriptions[i][1]);
else map1.get(descriptions[i][0]).right = map1.get(descriptions[i][1]);
parent.put(descriptions[i][1], descriptions[i][0]);
}
// 从任一节点开始,一直向上找自己的父节点,直到找到父节点为自己(即 头结点)
int x = descriptions[0][0];
while (x != parent.get(x)) {
x = parent.get(x);
}
return map1.get(x);
}
}
// 力扣大神 @arignote(https://leetcode-cn.com/u/arignote/) 解法
class Solution {
public TreeNode createBinaryTree(int[][] descriptions) {
HashMap<Integer, TreeNode> map = new HashMap<>();
// 同本人方法的挂节点
for (int[] description : descriptions) {
if (description[2] == 0) {
// computeIfAbsent() 方法对 hashMap 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hashMap 中。
map.computeIfAbsent(description[0], t -> new TreeNode(description[0])).right = map
.computeIfAbsent(description[1], t -> new TreeNode(description[1]));
} else {
map.computeIfAbsent(description[0], t -> new TreeNode(description[0])).left = map
.computeIfAbsent(description[1], t -> new TreeNode(description[1]));
}
}
// 思路不同之处!绝!!!
// 遍历descriptions,从map中删除作为子节点的key,即只剩最后一个头结点!
for (int[] description : descriptions) {
map.remove(description[1]);
}
return map.values().iterator().next();
}
}
小结: 多看最优解找思路!
4. 替换数组中的非互质数
描述:
给你一个整数数组 nums
。请你对数组执行下述操作:
- 从
nums
中找出 任意 两个 相邻 的 非互质 数。 - 如果不存在这样的数,终止 这一过程。
- 否则,删除这两个数,并 替换 为它们的 最小公倍数(Least Common Multiple,LCM)。
- 只要还能找出两个相邻的非互质数就继续 重复 这一过程。
返回修改后得到的 最终 数组。可以证明的是,以 任意 顺序替换相邻的非互质数都可以得到相同的结果。
生成的测试用例可以保证最终数组中的值 小于或者等于 108
。
两个数字 x
和 y
满足 非互质数 的条件是:GCD(x, y) > 1
,其中 GCD(x, y)
是 x
和 y
的 最大公约数 。
示例 1 :
输入:nums = [6,4,3,2,7,6,2]
输出:[12,7,6]
解释:
- (6, 4) 是一组非互质数,且 LCM(6, 4) = 12 。得到 nums = [12,3,2,7,6,2] 。
- (12, 3) 是一组非互质数,且 LCM(12, 3) = 12 。得到 nums = [12,2,7,6,2] 。
- (12, 2) 是一组非互质数,且 LCM(12, 2) = 12 。得到 nums = [12,7,6,2] 。
- (6, 2) 是一组非互质数,且 LCM(6, 2) = 6 。得到 nums = [12,7,6] 。
现在,nums 中不存在相邻的非互质数。
因此,修改后得到的最终数组是 [12,7,6] 。
注意,存在其他方法可以获得相同的最终数组。
示例 2 :
输入:nums = [2,2,1,1,3,3,3]
输出:[2,1,1,3]
解释:
- (3, 3) 是一组非互质数,且 LCM(3, 3) = 3 。得到 nums = [2,2,1,1,3,3] 。
- (3, 3) 是一组非互质数,且 LCM(3, 3) = 3 。得到 nums = [2,2,1,1,3] 。
- (2, 2) 是一组非互质数,且 LCM(2, 2) = 2 。得到 nums = [2,1,1,3] 。
现在,nums 中不存在相邻的非互质数。
因此,修改后得到的最终数组是 [2,1,1,3] 。
注意,存在其他方法可以获得相同的最终数组。
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 105
- 生成的测试用例可以保证最终数组中的值 小于或者等于
108
。
// 力扣大神 @arignote(https://leetcode-cn.com/u/arignote/) 解法
// java.math.BigInteger 用来表示任意大小的整数
import java.math.BigInteger;
class Solution {
public List<Integer> replaceNonCoprimes(int[] nums) {
ArrayList<Integer> list = new ArrayList<>();
for (int num : nums) {
// BigInteger里的 a.gcd(b)方法 求a和b的最大公约数
while (!list.isEmpty()
&& BigInteger.valueOf(num).gcd(BigInteger.valueOf(list.get(list.size() - 1))).intValue() > 1) {
num *= list.get(list.size() - 1)
/ BigInteger.valueOf(num).gcd(BigInteger.valueOf(list.remove(list.size() - 1))).intValue();
}
list.add(num);
}
return list;
}
}
小结: 对编程语言的各种内置函数的熟悉任重道远!