本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。

大家好,我是小彭。

昨晚是 LeetCode 第 98 场双周赛,你参加了吗?这场周赛需要脑筋急转弯,转不过来 Medium 就会变成 Hard,转得过来就变成 Easy。


小彭的 Android 交流群 02 群已经建立啦,公众号回复 “加群” 加入我们~


2566. 替换一个数字后的最大差值(Easy)

题目地址

https://leetcode.cn/problems/maximum-difference-by-remapping-a-digit/

题目描述

给你一个整数 num 。你知道 Danny Mittal 会偷偷将 0 到 9 中的一个数字 替换 成另一个数字。

请你返回将 num 中 恰好一个 数字进行替换后,得到的最大值和最小值的差位多少。

注意:

  • 当 Danny 将一个数字 d1 替换成另一个数字 d2 时,Danny 需要将 nums 中所有 d1 都替换成 d2 。
  • Danny 可以将一个数字替换成它自己,也就是说 num 可以不变。
  • Danny 可以将数字分别替换成两个不同的数字分别得到最大值和最小值。
  • 替换后得到的数字可以包含前导 0 。
  • Danny Mittal 获得周赛 326 前 10 名,让我们恭喜他。

题解(字符串操作)

  • 技巧:将整型转换为字符串能够更方便地修改具体位置。

简单模拟题,有 2 个思路:

  • 思路 1 - 暴力枚举:尝试枚举每类的数字,将其替换为 9 取得最大值,将其替换为 0 取得最小值,最后取所有方案的最大值和最小值取差值;
  • 思路 2 - 贪心思路:替换越靠近 “高位” 的数字能够使得差值越大,所以我们将从高位开始的首个非 9 数字替换为 9(例如 90 替换为 99)必然得到最大值,将从高位开始的首个数字替换为 0(例如 90 替换为 00)必然得到最小值。
// 思路 1
class Solution {
fun minMaxDifference(num: Int): Int {
val numStr = "$num"
var max = num
var min = num
for (element in numStr) {
max = Math.max(max, numStr.replace(element, '9').toInt())
min = Math.min(min, numStr.replace(element, '0').toInt())
}
return max - min
}
}

复杂度分析:

  • 时间复杂度:$O(log^2,{num})$ 数字最多有 log num 位,外层循环与内存循环的字符串替换操作都是 $O(log,{num})$ 时间级别复杂度;
  • 空间复杂度:$O(log,{num})$ 字符串占用空间。
// 思路 2
class Solution {
fun minMaxDifference(num: Int): Int {
val numStr = "$num"
val min = numStr.replace(numStr[0], '0').toInt()
var max = num
for (element in numStr) {
if ('9' != element) {
max = numStr.replace(element, '9').toInt()
break
}
}
return max - min
}
}

复杂度分析:

  • 时间复杂度:$O(log,{num})$ 内存循环的字符串替换操作最多只会执行一次,均摊下来整体只有 $O(log,{num})$ 级别的时间复杂度;
  • 空间复杂度:$O(log,{num})$ 字符串占用空间。

2567. 修改两个元素的最小分数(Medium)

题目地址

https://leetcode.cn/problems/minimum-score-by-changing-two-elements/

题目描述

给你一个下标从 0 开始的整数数组 nums 。

  • nums 的 最小 得分是满足 0 <= i < j < nums.length 的 |nums[i] - nums[j]| 的最小值。
  • nums的 最大 得分是满足 0 <= i < j < nums.length 的 |nums[i] - nums[j]| 的最大值。
  • nums 的分数是 最大 得分与 最小 得分的和。

我们的目标是最小化 nums 的分数。你 最多 可以修改 nums 中 2 个元素的值。

请你返回修改 nums 中 至多两个 元素的值后,可以得到的 最小分数 。

|x| 表示 x 的绝对值。

题解(排序 + 枚举)

这道题也有脑筋急转弯的成分,同时我们可以扩展思考下 “最多修改 k 个元素的最小得分” 问题,最后再说。

这道题的关键在于得分的定义:

  • “最小得分” 表示任意数组中两个数字之间的最小绝对差;
  • “最大得分” 表示任意数组中两个数字之间的最大绝对差。

理解题意后容易发现:

  • 影响 “最小得分” 的是数组中最接近的两个数字。当数组中存在两个相同元素时,“最小得分” 可以取到最小值 0;
  • 影响 “最大得分” 的是数组中最不接近的两个数,即最大值和最小值。当我们将最大值和最小值修改为数组中间的某个元素时,能使得差值变小的同时,保持 “最小得分” 取最小值 0。

因此得知: 这道题的关键点在于修改数组的最大值或最小值成为数组中间的某个元素。 要么让最大值变小,要么让最小值变大。由于题目最多只能修改 2 次,因此最多只能以下 3 种情况:

  • 情况 1:修改数组中最大的两个数为 nums[n - 3]
  • 情况 2:修改数组中最小的两个数为 nums[2]
  • 情况 3:修改数组的最大值为 nums[n - 1],修改数组的最小值为 nums[1]

简单枚举出 3 种情况的解后再进行一轮比较即可。

最后再观察边界条件,数组的最小长度为 3,所以不需要特判。

class Solution {
fun minimizeSum(nums: IntArray): Int {
nums.sort()
val n = nums.size
val choice1 = nums[n - 3] - nums[0]
val choice2 = nums[n - 1] - nums[2]
val choice3 = nums[n - 2] - nums[1]
return Math.min(choice1, Math.min(choice2, choice3))
}
}

复杂度分析:

  • 时间复杂度:$O(nlgn)$ 快速排序占用的时间,如果手动维护最小的 3 个元素和最大的 3 个元素可以降低到 $O(n)$ 时间复杂度;
  • 空间复杂度:$O(lgn)$ 排序占用的递归栈空间。

再扩展思考一下,如果题目说明最多可以修改 $k (0 ≤ k ≤ nums.length)$次的话,应该解决问题呢? —— 即 “求最多修改 k 个元素的最小得分”,原题就是 k = 2 的情况。

那么这道题就是考察 “滑动窗口” 技巧了,我们可以将修改的范围视为一个跨越数组首尾且长度为 k 的滑动窗口,那么而问题的答案就取决于 “不被” 滑动窗口包围的另一部分。再逆向思考一下,我们可以用长度为 length - k 的滑动窗口在数组上移动,并记录窗口首尾元素的差值,枚举所有情况后记录最小值即为最小得分:

举个例子,在输入数组为 [1, 4, 5, 7, 8] ,k = 2 时,前文提到的 3 种方案分别对应以下 3 个窗口状态:

  • 情况 1:修改数组中最大的两个数:1,4 | 5,7,8 |
  • 情况 2:修改数组中最小的两个数:| 1,4,5 | 7,8
  • 情况 3:修改数组的最大值和最小值:1 | 4,5,7 | 8
class Solution {
fun minimizeSum(nums: IntArray): Int {
val n = nums.size
// 操作次数
val k = 2
// 滑动窗口
val len = n - k
nums.sort()
var min = Integer.MAX_VALUE
for (left in 0..n - len) {
val right = left + len - 1
min = Math.min(min, nums[right] - nums[left])
}
return min
}
}

复杂度分析同上。


2568. 最小无法得到的或值(Medium)

题目地址

https://leetcode.cn/problems/minimum-impossible-or/

题目描述

给你一个下标从 0 开始的整数数组 nums 。

如果存在一些整数满足 0 <= index1 < index2 < ... < indexk < nums.length ,得到 nums[index1] | nums[index2] | ... | nums[indexk] = x ,那么我们说 x 是 可表达的 。换言之,如果一个整数能由 nums 的某个子序列的或运算得到,那么它就是可表达的。

请你返回 nums 不可表达的 最小非零整数 。

题解一(散列表)

相似题目:2154. 将找到的值乘以 2

这道题需要脑筋急转弯。

首先,我们先观察输入数据范围中小数值的二进制表示,尝试发现规律:

  • 0 = 0000 = 0
  • 1 = 0001 = 1
  • 2 = 0010 = 2
  • 3 = 0011 = 2 | 1
  • 4 = 0100 = 4
  • 5 = 0101 = 4 | 1
  • 6 = 0110 = 4 | 2
  • 7 = 0111 = 4 | 2 | 1,或者 5 | 1
  • 8 = 1000 = 8
  • 9 = 1001 = 8 | 1
  • 10 = 1010 = 8 | 2

我们发现以下 2 点信息:

  • 除了数字 7 = 5 | 1 的特殊方案外,其他数字的表示方案都可以由形如 $x = 2^i | 2^j | 2^ k$ 的格式表达(很容易理解);
  • $2^i$ 格式的数字不可能被其他数用 “或” 的形式表示(也很容易理解)。

由此可以得出结论: 影响数组最小可表达数的关键在于数组中 “未出现的最小的 $2^i$”,并且这个数就是不可表达的最小非零数。

举例说明:假设 8 是数组中未出现的最小 $2^i$(此时 [1, 2, 4] 肯定在数组中出现$2^i$),那么数字 1 ~ 7 之间的所有数字都可以由 [1、2、4] 通过或表示,而 8 无法被 [1, 2, 3, 4, 5, 6 ,7] 之间的任何数字表达,同时也无法被大于 8 的其他数表示,因此 8 就是最小的可表达数。

完成问题转换后编码就很容易了,我们只要从小到大枚举所有 $2^i$ ,并检查它是否在数组中出现即可:

class Solution {
fun minImpossibleOR(nums: IntArray): Int {
val numSet = nums.toHashSet()
var i = 1
while (numSet.contains(i)) {
i = i shl 1
}
return i
}
}

复杂度分析:

  • 时间复杂度:$O(n + logU)$ 其中 n 是数组长度,U 是数组的最大值,最多只需要检查 logU 位数字;
  • 空间复杂度:$O(n)$ 散列表占用的空间。

题解二(位运算)

题解一使用散列表来辅助判断 $2^i$ 是否存在于数组中,可以进一步优化:我们将直接从数组元素的二进制数据中提取特征值,并还原出 “未出现的最小的 $2^i$”:

  • 1、遍历数组中所有元素,如果元素值是 $2^i$ 则将其记录到 mask 特征值中;
  • 2、遍历结束后将得到形如 0011, 1011 格式的特征值,此时 “未出现的最小的 $2^i$” 正好位于从低位到高位出现的首个 0 的位置,即 0000, 0100;
  • 3、为了还原出目标数,执行以下位运算:
x = ~x // 按位取反:    0011,1011 => 1100,0100
x & -x // lowbit 公式:1100,0100 => 0000,0100
class Solution {
fun minImpossibleOR(nums: IntArray): Int {
var mask = 0
for (x in nums) {
// x & (x - 1) 将消除最低位的 1,如果消除后值为 1 说明 x 本身就是 2 的幂
if (x and (x - 1) == 0) mask = mask or x
}
// 取反
mask = mask.inv()
// 取最低位 1
return mask and -mask
}
}

复杂度分析:

  • 时间复杂度:$O(n)$ 其中 n 是数组长度;
  • 空间复杂度:$O(1)$ 仅占用常数级别空间。

2569. 更新数组后处理求和查询(Hard)

题目地址

https://leetcode.cn/problems/handling-sum-queries-after-update/

题目描述

给你两个下标从 0 开始的数组 nums1 和 nums2 ,和一个二维数组 queries 表示一些操作。总共有 3 种类型的操作:

  1. 操作类型 1 为 queries[i] = [1, l, r] 。你需要将 nums1 从下标 l 到下标 r 的所有 0 反转成 1 或将 1 反转成 0 。l 和 r 下标都从 0 开始。
  2. 操作类型 2 为 queries[i] = [2, p, 0] 。对于 0 <= i < n 中的所有下标,令 nums2[i] = nums2[i] + nums1[i] * p 。
  3. 操作类型 3 为 queries[i] = [3, 0, 0] 。求 nums2 中所有元素的和。

请你返回一个数组,包含所有第三种操作类型的答案。

预备知识

类似的区间求和问题,我们先回顾一下解决方案:

  • 1、静态数组求区间和:「前缀和数组」、「树状数组」、「线段树」
  • 2、频繁单点更新,求区间和:「树状数组」、「线段树」
  • 3、频繁区间更新,求具体位置:「差分数组」
  • 4、频繁区间更新,求区间和:「线段树 + 懒更新」

这道题涉及 “区间更新” 和 “区间求和”,所以属于线段树的典型例题。

题解一(朴素线段树)

我们先理解题目中三种操作的含义:

  • 操作一:对 nums1 数组中位于 [left, right] 区间的数进行反转,也就是进行 “区间更新”;
  • 操作二:将 nums1 数组上的数值 nums1[index] 乘以 p 后累加到 nums2 数组的相同位置上,即 nums2[index] += nums1[index] * p,同样也是进行 “区间更新”;
  • 操作三:求 nums2 数组中所有元素和,即 “求区间和”。

OK,既然操作一和操作二是对不同数组进行 “区间更新”,那么我们需要分别为这两个数组建立线段树吗?并不需要,这是题目抛出的烟雾弹。

因为题目最终的解是求 nums2 数组的全体和,所以我们并不需要真正地维护 nums2 数组,只需要将操作二的增量累加到全体和中。这样的话就是只需要维护 nums1 数组的线段树。

理解题意后,我们可以写出题解的主框架:

  • 1、首先计算 nums2 数组的初始全体和 sum
  • 2、建立 nums1 数组的线段树;
  • 3、依次处理每种操作,操作一对线段树做区间更新,操作二对线段树做区间求和后乘以 p,并累加到全体和 sum 中,操作三将 sum 推入结果列表。
// 程序主框架
class Solution {
fun handleQuery(nums1: IntArray, nums2: IntArray, queries: Array<IntArray>): LongArray {
val n = nums1.size
val resultList = LinkedList<Long>()
// 全体和
var sum = 0L
for (num in nums2) {
sum += num
}
val tree = SegementTree(nums1)
for (query in queries) {
when (query[0]) {
1 -> {
// 区间更新
tree.update(query[1], query[2])
}
2 -> {
// 求区间和(nums[index] * p)
sum += 1L * query[1] * tree.query(0, n - 1)
}
3 -> {
// 记录
resultList.add(sum)
}
}
}
return resultList.toLongArray()
} private class SegementTree(private val data: IntArray) { // 区间更新(反转)
fun update(left: Int, right: Int) { } // 单点更新(反转)- 本题不需要
fun set(pos: Int) { } // 区间查询
fun query(left: Int, right: Int): Int { }
}
}

接下来就是实现线段树的内部代码了。

  • 技巧 1:这道题的更新操作是对 0/ 1 反转,我们可以用异或来实现;
  • 技巧 2:相对于在函数中重复传递节点所代表的区间范围(例如 update(i: int, l: int, r: int, L: int, R: int)),使用 Node 节点记录更为方便。
class Solution {
fun handleQuery(nums1: IntArray, nums2: IntArray, queries: Array<IntArray>): LongArray {
val n = nums1.size
val resultList = LinkedList<Long>()
// 全体和
var sum = 0L
for (num in nums2) {
sum += num
}
val tree = SegementTree(nums1)
for (query in queries) {
when (query[0]) {
1 -> {
// 区间更新
tree.update(query[1], query[2])
}
2 -> {
// 求区间和(nums[index] * p)
sum += 1L * query[1] * tree.query(0, n - 1)
}
3 -> {
// 记录
resultList.add(sum)
}
}
}
return resultList.toLongArray()
} private class SegementTree(private val data: IntArray) {
// 线段树节点(区间范围与区间值)
private class Node(val left: Int, val right: Int, var value: Int) // 线段树数组
private val tree = Array<Node?>(4 * data.size) { null } as Array<Node> // 左子节点的索引
private val Int.left get() = this * 2 + 1 // 右子节点的索引
private val Int.right get() = this * 2 + 2 init {
// 建树
buildNode(0, 0, data.size - 1)
} // 构建线段树节点
private fun buildNode(index: Int, left: Int, right: Int) {
if (left == right) {
// 叶子节点
tree[index] = Node(left, right, data[left])
return
}
val mid = (left + right) ushr 1
// 构建左子节点
buildNode(index.left, left, mid)
// 构建左子节点
buildNode(index.right, mid + 1, right)
// 合并左右子节点
tree[index] = Node(left, right, tree[index.left].value + tree[index.right].value)
} // 区间更新(反转)
fun update(left: Int, right: Int) {
update(0, left, right)
} // 区间更新(反转)
private fun update(index: Int, left: Int, right: Int) {
// 1、当前节点不处于区间范围内
if (tree[index].left > right || tree[index].right < left) return
// 2、叶子节点
if (tree[index].left == tree[index].right) {
// 反转:0->1,1->0
tree[index].value = tree[index].value xor 1
return
}
// 3、更新左子树
update(index.left, left, right)
// 4、更新右子树
update(index.right, left, right)
// 5、合并子节点的结果
tree[index].value = tree[index.left].value + tree[index.right].value
} // 单点更新(反转)- 本题不需要
fun set(pos: Int) {
set(0, pos)
} // 单点更新(反转)- 本题不需要
private fun set(index: Int, pos: Int) {
// 1、当前节点不处于区间范围内
if (tree[index].left > pos || tree[index].right < pos) return
// 2、叶子节点
if (tree[index].left == tree[index].right) {
// 反转:0->1,1->0
tree[index].value = tree[index].value xor 1
return
}
// 3、更新左子树
set(index.left, pos)
// 4、更新右子树
set(index.right, pos)
// 5、合并子节点的结果
tree[index].value = tree[index.left].value + tree[index.right].value
} // 区间查询
fun query(left: Int, right: Int): Int {
return query(0, left, right)
} // 区间查询
private fun query(index: Int, left: Int, right: Int): Int {
// 1、当前节点不处于区间范围内
if (tree[index].left > right || tree[index].right < left) return 0
// 2、当前节点完全处于区间范围之内
if (tree[index].left >= left && tree[index].right <= right) return tree[index].value
// 3、合并子节点的结果
return query(index.left, left, right) + query(index.right, left, right)
}
}
}

复杂度分析:

  • 时间复杂度:$O(n + q_1n + q_2)$ 其中 n 是 nums1 数组长度,$q_1$ 是操作一的个数,$q_2$ 是操作二的个数。我们需要花费 $O(n)$ 时间建树,操作一线段树区间更新的时间复杂度是 $O(n)$,操作二线段树区间查询的复杂度是 $O(lgn)$,但本题中的查询正好是线段树根节点,所以操作二实际上只需要 $O(1)$ 复杂度。
  • 空间复杂度:$O(n)$ 线段树空间。

朴素线段树解法在本题中会超时,我们需要优化为 “懒更新” 的线段树实现。

题解二(线段树 + 懒更新)

朴素线段树的性能瓶颈在于:区间更新需要改动从根节点到叶子节点中所有与目标区间有交集的节点,因此单次区间更新操作的时间复杂度是 $O(n)$。

懒更新线段树的核心思想是:当一个节点代表的区间完全包含于目标区间内时,我们没有必要继续向下递归更新,而是在当前节点上标记 Lazy Tag 。只有将来更新该节点的某个子区间时,才会将懒更新 pushdown 到子区间。

举个例子:在长度为 10 的线段树中执行 [1,10][1,5] 两次区间更新操作(对区间内的元素加一):

  • [1,10] 区间更新:从根节点出发,此时发现根节点与目标区间 [1,10] 完全相同,那么只更新根节点并标记 Lazy Tag,更新结束;
  • [1,5] 区间更新:从根节点出发,此时发现根节点有 Lazy Tag,那么需要先将懒更新 pushdown[1,5][6,10] 两个子节点,然后再更新 [1,5] 区间。
  • 到目前为止,[1,10][1,5] 节点被修改 2 次,[6,10] 节点被修改 1 次,其它节点没有被修改。

接下来就是实现线段树的内部代码了。

  • 技巧 1:0 /1 反转是负负得正的,所以 Lazy Tag 可以用 Boolean 类型表示,true 表示被反转;
  • 技巧 2:区间反转可以用区间长度 - 旧值实现,即:value = right - left + 1 - value

提示:相比题解一改动的函数有 【懒更新】 标记 。

class Solution {
fun handleQuery(nums1: IntArray, nums2: IntArray, queries: Array<IntArray>): LongArray {
val n = nums1.size
val resultList = LinkedList<Long>()
// 全体和
var sum = 0L
for (num in nums2) {
sum += num
}
val tree = LazySegementTree(nums1)
for (query in queries) {
when (query[0]) {
1 -> {
// 区间更新
tree.update(query[1], query[2])
}
2 -> {
// 求区间和(nums[index] * p)
sum += 1L * query[1] * tree.query(0, n - 1)
}
3 -> {
// 记录
resultList.add(sum)
}
}
}
return resultList.toLongArray()
} private class LazySegementTree(private val data: IntArray) {
// 线段树节点(区间范围与区间值)【懒更新】
private class Node(val left: Int, val right: Int, var value: Int, var lazy: Boolean = false) // 线段树数组
private val tree = Array<Node?>(4 * data.size) { null } as Array<Node> // 左子节点的索引
private val Int.left get() = this * 2 + 1 // 右子节点的索引
private val Int.right get() = this * 2 + 2 init {
// 建树
buildNode(0, 0, data.size - 1)
} // 构建线段树节点
private fun buildNode(index: Int, left: Int, right: Int) {
if (left == right) {
// 叶子节点
tree[index] = Node(left, right, data[left])
return
}
val mid = (left + right) ushr 1
// 构建左子节点
buildNode(index.left, left, mid)
// 构建左子节点
buildNode(index.right, mid + 1, right)
// 合并左右子节点
tree[index] = Node(left, right, tree[index.left].value + tree[index.right].value)
} // 区间更新(反转)
fun update(left: Int, right: Int) {
update(0, left, right)
} // 区间更新(反转)【懒更新】
private fun update(index: Int, left: Int, right: Int) {
// 1、当前节点不处于区间范围内
if (tree[index].left > right || tree[index].right < left) return
// 2、当前节点完全处于区间范围之内
if (tree[index].left >= left && tree[index].right <= right) {
lazyUpdate(index)
return
}
// 3、pushdown 到子节点
if (tree[index].lazy) {
lazyUpdate(index.left)
lazyUpdate(index.right)
tree[index].lazy = false
}
// 4、更新左子树
update(index.left, left, right)
// 5、更新右子树
update(index.right, left, right)
// 6、合并子节点的结果
tree[index].value = tree[index.left].value + tree[index.right].value
} // 单点更新(反转)- 本题不需要
fun set(pos: Int) {
set(0, pos)
} // 单点更新(反转)【懒更新】- 本题不需要
private fun set(index: Int, pos: Int) {
// 1、当前节点不处于区间范围内
if (tree[index].left > pos || tree[index].right < pos) return
// 2、叶子节点
if (tree[index].left == tree[index].right) {
lazyUpdate(index)
return
}
// 3、pushdown 到子节点
if (tree[index].lazy) {
lazyUpdate(index.left)
lazyUpdate(index.right)
tree[index].lazy = false
}
// 4、更新左子树
set(index.left, pos)
// 5、更新右子树
set(index.right, pos)
// 6、合并子节点的结果
tree[index].value = tree[index.left].value + tree[index.right].value
} // 区间查询
fun query(left: Int, right: Int): Int {
return query(0, left, right)
} // 区间查询
private fun query(index: Int, left: Int, right: Int): Int {
// 1、当前节点不处于区间范围内
if (tree[index].left > right || tree[index].right < left) return 0
// 2、当前节点完全处于区间范围之内
if (tree[index].left >= left && tree[index].right <= right) return tree[index].value
// 3、pushdown 到子节点
if (tree[index].lazy) {
lazyUpdate(index.left)
lazyUpdate(index.right)
tree[index].lazy = false
}
// 4、合并子节点的结果
return query(index.left, left, right) + query(index.right, left, right)
} // 懒更新
private fun lazyUpdate(index: Int) {
// 反转
tree[index].value = tree[index].right - tree[index].left + 1 - tree[index].value
// 标记(负负得正)
tree[index].lazy = !tree[index].lazy
}
}
}

复杂度分析:

  • 时间复杂度:$O(n + q_1lgn + q_2)$ 其中 n 是 nums1 数组长度,$q_1$ 是操作一的个数,$q_2$ 是操作二的个数。
  • 空间复杂度:$O(n)$ 线段树空间。

LeetCode 双周赛 98,脑筋急转弯转不过来!的更多相关文章

  1. leetcode 双周赛9 进击的骑士

    一个坐标可以从 -infinity 延伸到 +infinity 的 无限大的 棋盘上,你的 骑士 驻扎在坐标为 [0, 0] 的方格里. 骑士的走法和中国象棋中的马相似,走 “日” 字:即先向左(或右 ...

  2. leetcode 双周赛9 找出所有行中最小公共元素

    给你一个矩阵 mat,其中每一行的元素都已经按 递增 顺序排好了.请你帮忙找出在所有这些行中 最小的公共元素. 如果矩阵中没有这样的公共元素,就请返回 -1. 示例: 输入:mat = [[,,,,] ...

  3. [每日一题2020.06.16] leetcode双周赛T3 5423 找两个和为目标值且不重叠的子数组 DP, 前缀和

    题目链接 给你一个整数数组 arr 和一个整数值 target . 请你在 arr 中找 两个互不重叠的子数组 且它们的和都等于 target .可能会有多种方案,请你返回满足要求的两个子数组长度和的 ...

  4. LeetCode双周赛#36

    1604. 警告一小时内使用相同员工卡大于等于三次的人 题目链接 题意 给定两个字符串数组keyName和keyTime,分别表示名字为keytime[i]的人,在某一天内使用员工卡的时间(格式为24 ...

  5. LeetCode双周赛#35

    1589. 所有排列中的最大和 #差分 #贪心 题目链接 题意 给定整数数组nums,以及查询数组requests,其中requests[i] = [starti, endi] .第i个查询求 num ...

  6. LeetCode双周赛#34

    5492. 分割字符串的方案数 #组合公式 #乘法原理 #区间分割 题目链接 题意 给定01二进制串\(s\),可将\(s\)分割为三个非空 字符串\(s_1,s_2,s_3\),即(\(s_1+s_ ...

  7. LeetCode双周赛#33 题解

    5480. 可以到达所有点的最少点数目 #贪心 题目链接 题意 给定有向无环图,编号从0到n-1,一个边集数组edges(表示从某个顶点到另一顶点的有向边),现要找到最小的顶点集合,使得从这些点出发, ...

  8. Leetcode 双周赛#32 题解

    1540 K次操作转变字符串 #计数 题目链接 题意 给定两字符串\(s\)和\(t\),要求你在\(k\)次操作以内将字符串\(s\)转变为\(t\),其中第\(i\)次操作时,可选择如下操作: 选 ...

  9. LeetCode第8场双周赛(Java)

    这次我只做对一题. 原因是题目返回值类型有误,写的是 String[] ,实际上应该返回 List<String> . 好吧,只能自认倒霉.就当涨涨经验. 5068. 前后拼接 解题思路 ...

  10. LeetCode 第 15 场双周赛

    1287.有序数组中出现次数超过25%的元素 1288.删除被覆盖区间 1286.字母组合迭代器 1289.下降路径最小和 II 下降和不能只保留原数组中最小的两个,hacked. 1287.有序数组 ...

随机推荐

  1. (GDB) GDB调试技巧,调试命令

    调试时查看依赖DSO pidof tvm_rpc_server cat /proc/<pid_of_tvm_rpc_server>/maps 子进程调试 1.vscode -- launc ...

  2. Datawhale组队学习_Task01:概览西瓜书+南瓜书第1、2章

    第一章 绪论 1.1引言 打开书,五分钟后,合上书:嗯!学会了!如何判断一个好瓜 1.2基本术语 分类:对离散值进行预测的学习任务,是有监督学习的代表 回归:对连续值进行预测的学习任务,是有监督学习的 ...

  3. 【每日一题】【递归+int型返回值最后不接收】110. 平衡二叉树-211231/220221

    给定一个二叉树,判断它是否是高度平衡的二叉树. 本题中,一棵高度平衡二叉树定义为: 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 . 答案: public class Solution ...

  4. 【大数据面试】ClickHouse:介绍、特点、数据类型、引擎、操作、副本、分片

    1.介绍 开源的列式存储数据库(DBMS),由C++编写,用于在线分析处理查询(OLAP) 可以通过SQL查询实时生成分析数据报告 解释: DBMS:数据库管理系统 常见的列式存储数据库:Hbase. ...

  5. 【每日一题】【双指针/栈/reverse】2022年2月19日-判断是否为回文字符串

    给定一个长度为 n 的字符串,请编写一个函数判断该字符串是否回文.如果是回文请返回true,否则返回false.   字符串回文指该字符串正序与其逆序逐字符一致.   数据范围:0 < n \l ...

  6. MasaFramework -- 领域驱动设计

    概念 什么是领域驱动设计 领域驱动的主要思想是, 利用确定的业务模型来指导业务与应用的设计和实现.主张开发人员与业务人员持续地沟通和模型的持续迭代,从而保证业务模型与代码的一致性,实现有效管理业务的复 ...

  7. include指令和include动作的区别

    include指令和<jsp:include>动作标识的区别 1.include指令通过file属性指定被包含的文件,并且file属性不支持任何表达式: <jsp:include&g ...

  8. django中如何开启事务

    一:django中如何开启事务 1.事务的四大特征 ACID A: 原子性 每个事务都是不可分割的最小单位(同一个事物内的多个操作要么同时成功要么同时失败) C: 一致性 事物必须是使数据库从一个一致 ...

  9. Jmeter 之连接数据库

    1.下载mysql-connector-java-5.1.7-bin.jar 2.下载后将该jar包放于bin目录下,如:D:\Program Files\apache-jmeter-5.2\bin ...

  10. [编程基础] C++多线程入门7-条件变量介绍

    原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++11标准. 文章目录 7 条件变 ...