LeetCode Go · 哈希表(8) ​
来源与许可
题目版权归 LeetCode;题解来自 Doocs/leetcode,按 CC BY-SA 4.0 共享。补充 Go 代码来自灵茶山艾府、halfrost,保留 MIT 许可及逐题署名。本站提取 Go、调整分类及折叠排版;标注“本站整理”的 Go 解法依据题意与参考思路补写,按 CC BY-SA 4.0 共享。收录日期:2026-10-31;主题源版本:91ef8c241a5e。
本卷 30 题 · 点击题目展开,先独立思考,再查看解法。
3581. 计算数字中的奇数字母数量 🔒 ​
题目、示例与约束
你被给定一个整数 n,执行以下步骤:
- 将
n的每个数位转换为它的小写英文单词(例如 4 → "four", 1 → "one")。 - 将那些单词按照 原始数字顺序 连接 起来形成一个字符串
s。
返回字符串 s 中出现 奇数 次的 不同 字符的数量。
示例 1:
输入:n = 41
输出:5
解释:
41 → "fourone"
出现奇数次的字母:'f','u','r','n','e'。因此,答案为 5。
示例 2:
输入:n = 20
输出:5
解释:
20 → "twozero"
出现奇数次的字母:'t','w','z','e','r'。因此,答案为 5。
提示:
1 <= n <= 109
方法一:模拟 + 位运算 · Go 实现
思考
把 的各位写成英文后,统计出现奇数次的字母种数。字母只有 个,可用一个整数位掩码表示奇偶。
每读入一个字母就异或对应位,最后 即奇数次字母个数。不必真正拼接长串。
我们可以将每个数字转换为对应的英文单词,然后统计每个字母出现的次数。由于字母的数量有限,我们可以使用一个整数 来表示每个字母的出现情况。具体地,我们可以将字母映射到整数的二进制位上,如果某个字母出现了奇数次,则对应的二进制位为 1,否则为 0。最后,我们只需要统计 中为 1 的位数,即为答案。
时间复杂度 ,其中 是输入的整数。空间复杂度 。
func countOddLetters(n int) int {
d := map[int]string{
0: "zero",
1: "one",
2: "two",
3: "three",
4: "four",
5: "five",
6: "six",
7: "seven",
8: "eight",
9: "nine",
}
mask := 0
for n > 0 {
x := n % 10
n /= 10
for _, c := range d[x] {
mask ^= 1 << (c - 'a')
}
}
return bits.OnesCount32(uint32(mask))
}
3583. 统计特殊三元组 ​
题目、示例与约束
给你一个整数数组 nums。
特殊三元组 定义为满足以下条件的下标三元组 (i, j, k):
0 <= i < j < k < n,其中n = nums.lengthnums[i] == nums[j] * 2nums[k] == nums[j] * 2
返回数组中 特殊三元组 的总数。
由于答案可能非常大,请返回结果对 109 + 7 取余数后的值。
示例 1:
输入: nums = [6,3,6]
输出: 1
解释:
唯一的特殊三元组是 (i, j, k) = (0, 1, 2),其中:
nums[0] = 6,nums[1] = 3,nums[2] = 6nums[0] = nums[1] * 2 = 3 * 2 = 6nums[2] = nums[1] * 2 = 3 * 2 = 6
示例 2:
输入: nums = [0,1,0,0]
输出: 1
解释:
唯一的特殊三元组是 (i, j, k) = (0, 2, 3),其中:
nums[0] = 0,nums[2] = 0,nums[3] = 0nums[0] = nums[2] * 2 = 0 * 2 = 0nums[3] = nums[2] * 2 = 0 * 2 = 0
示例 3:
输入: nums = [8,4,2,8,4]
输出: 2
解释:
共有两个特殊三元组:
(i, j, k) = (0, 1, 3)nums[0] = 8,nums[1] = 4,nums[3] = 8nums[0] = nums[1] * 2 = 4 * 2 = 8nums[3] = nums[1] * 2 = 4 * 2 = 8
(i, j, k) = (1, 2, 4)nums[1] = 4,nums[2] = 2,nums[4] = 4nums[1] = nums[2] * 2 = 2 * 2 = 4nums[4] = nums[2] * 2 = 2 * 2 = 4
提示:
3 <= n == nums.length <= 1050 <= nums[i] <= 105
方法一:枚举中间数字 + 哈希表 · Go 实现
思考
特殊三元组满足 。枚举两端为 ;改为枚举中间 ,左右 的出现次数之积即贡献。
先把全体计入 ,从左扫 :先从右侧减去当前,累加 ,再把 加入左侧。模 。
我们可以枚举中间数字 ,用两个哈希表 和 分别记录 左侧和右侧的数字出现次数。
我们首先将所有数字加入 中,然后从左到右遍历每个数字 ,在遍历过程中:
- 将 从 中移除。
- 计算 左侧的数字 的出现次数,记为 。
- 计算 右侧的数字 的出现次数,记为 。
- 将 和 相乘,得到以 为中间数字的特殊三元组数量,并将结果累加到答案中。
- 将 加入 中。
最后返回答案。
时间复杂度为 ,空间复杂度为 ,其中 是数组 的长度。
func specialTriplets(nums []int) int {
left := make(map[int]int)
right := make(map[int]int)
for _, x := range nums {
right[x]++
}
ans := int64(0)
mod := int64(1e9 + 7)
for _, x := range nums {
right[x]--
ans = (ans + int64(left[x*2])*int64(right[x*2])%mod) % mod
left[x]++
}
return int(ans)
}
3606. 优惠券校验器 ​
题目、示例与约束
给你三个长度为 n 的数组,分别描述 n 个优惠券的属性:code、businessLine 和 isActive。其中,第 i 个优惠券具有以下属性:
code[i]:一个 字符串,表示优惠券的标识符。businessLine[i]:一个 字符串,表示优惠券所属的业务类别。isActive[i]:一个 布尔值,表示优惠券是否当前有效。
当以下所有条件都满足时,优惠券被认为是 有效的 :
code[i]不能为空,并且仅由字母数字字符(a-z、A-Z、0-9)和下划线(_)组成。businessLine[i]必须是以下四个类别之一:"electronics"、"grocery"、"pharmacy"、"restaurant"。isActive[i]为 true 。
返回所有 有效优惠券的标识符 组成的数组,按照以下规则排序:
- 先按照其 businessLine 的顺序排序:
"electronics"、"grocery"、"pharmacy"、"restaurant"。 - 在每个类别内,再按照 标识符的字典序(升序)排序。
示例 1:
输入: code = ["SAVE20","","PHARMA5","SAVE@20"], businessLine = ["restaurant","grocery","pharmacy","restaurant"], isActive = [true,true,true,true]
输出: ["PHARMA5","SAVE20"]
解释:
- 第一个优惠券有效。
- 第二个优惠券的标识符为空(无效)。
- 第三个优惠券有效。
- 第四个优惠券的标识符包含特殊字符
@(无效)。
示例 2:
输入: code = ["GROCERY15","ELECTRONICS_50","DISCOUNT10"], businessLine = ["grocery","electronics","invalid"], isActive = [false,true,true]
输出: ["ELECTRONICS_50"]
解释:
- 第一个优惠券无效,因为它未激活。
- 第二个优惠券有效。
- 第三个优惠券无效,因为其业务类别无效。
提示:
n == code.length == businessLine.length == isActive.length1 <= n <= 1000 <= code[i].length, businessLine[i].length <= 100code[i]和businessLine[i]由可打印的 ASCII 字符组成。isActive[i]的值为true或false。
方法一:模拟 · Go 实现
思考
,直接按规则过滤即可,无需更复杂的索引结构。有效券须标识符非空且仅含字母、数字与下划线,业务属于给定四类,且处于激活状态。
先收集满足条件的下标,再按 排序,最后按下标取出标识符。排序键与题目的类别优先级、字典序要求一致。
我们可以直接模拟题目中的条件来筛选出有效的优惠券。具体步骤如下:
- 检查标识符:对于每个优惠券的标识符,检查它是否非空,并且只包含字母、数字和下划线。
- 检查业务类别:检查每个优惠券的业务类别是否属于给定的四个有效类别之一。
- 检查激活状态:检查每个优惠券是否处于激活状态。
- 收集有效优惠券:将所有满足上述条件的优惠券的 id 收集起来。
- 排序:根据业务类别和标识符对有效优惠券进行排序。
- 返回结果:返回排序后的有效优惠券的标识符列表。
时间复杂度 ,空间复杂度 ,其中 是优惠券的数量。
func validateCoupons(code []string, businessLine []string, isActive []bool) []string {
idx := []int{}
bs := map[string]struct{}{
"electronics": {},
"grocery": {},
"pharmacy": {},
"restaurant": {},
}
check := func(s string) bool {
if len(s) == 0 {
return false
}
for _, c := range s {
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' {
return false
}
}
return true
}
for i := range code {
if isActive[i] {
if _, ok := bs[businessLine[i]]; ok && check(code[i]) {
idx = append(idx, i)
}
}
}
sort.Slice(idx, func(i, j int) bool {
if businessLine[idx[i]] != businessLine[idx[j]] {
return businessLine[idx[i]] < businessLine[idx[j]]
}
return code[idx[i]] < code[idx[j]]
})
ans := make([]string, 0, len(idx))
for _, i := range idx {
ans = append(ans, code[i])
}
return ans
}
3659. 数组元素分组 ​
题目、示例与约束
给你一个整数数组 nums 和一个整数 k。
请你判断是否可以将 nums 中的所有元素分成一个或多个组,使得:
- 每个组 恰好 包含
k个元素。 - 每组中的元素 互不相同。
nums中的每个元素 必须 被分配到 恰好一个 组中。
如果可以完成这样的分组,返回 true;否则,返回 false。
示例 1:
输入: nums = [1,2,3,4], k = 2
输出: true
解释:
一种可能的分组方式是分成 2 组:
- 组 1:
[1, 2] - 组 2:
[3, 4]
每个组包含 k = 2 个不同的元素,并且所有元素都被恰好使用一次。
示例 2:
输入: nums = [3,5,2,2], k = 2
输出: true
解释:
一种可能的分组方式是分成 2 组:
- 组 1:
[2, 3] - 组 2:
[2, 5]
每个组包含 k = 2 个不同的元素,并且所有元素都被恰好使用一次。
示例 3:
输入: nums = [1,5,2,3], k = 3
输出: false
解释:
无法用所有值恰好一次性组成含有 k = 3 个不同元素的组。
提示:
1 <= nums.length <= 1051 <= nums[i] <= 1051 <= k <= nums.length
方法一:计数 · Go 实现
思考
要分成若干组,每组恰好 个不同元素。 若不能被 整除则组数不是整数,直接失败。
组数为 。同一值不能在一组出现两次,故任一值的次数不能超过 ,否则无法分到不同组。
该上界也充分:次数都不超过 时,可轮转分配到 组。计数后取最大频次与 比较。
我们记数组的长度为 。如果 不能被 整除,那么无法将数组分成若干个每组包含 个元素的组,直接返回 。
接下来,我们计算每组的大小 ,并统计数组中每个元素的出现次数。如果某个元素的出现次数超过了 ,那么也无法将其分到任何一组中,直接返回 。
最后,如果所有元素的出现次数都不超过 ,那么就可以将数组分成若干个每组包含 个元素的组,返回 。
时间复杂度 ,空间复杂度 或 。其中 是数组的长度,而 是数组中元素的最大值。
func partitionArray(nums []int, k int) bool {
n := len(nums)
if n%k != 0 {
return false
}
m := n / k
mx := slices.Max(nums)
cnt := make([]int, mx+1)
for _, x := range nums {
if cnt[x]++; cnt[x] > m {
return false
}
}
return true
}
3662. 按频率筛选字符 🔒 ​
题目、示例与约束
给定一个包含小写英文字母的字符串 s 和一个整数 k。
你的任务是构造一个新的字符串,其中只包含在整个字符串 s 中出现次数 少于 k 次的字符。新字符串中字符的顺序必须与 s 中的 顺序相同。
返回结果字符串。如果没有字符满足,返回一个空字符串。
注意:出现次数少于 k 次的字符的每次出现都被保留。
示例 1:
输入:s = "aadbbcccca", k = 3
输出:"dbb"
解释:
s 中字符出现的频率:
'a'出现 3 次'd'出现 1 次'b'出现 2 次'c'出现 4 次
只有 'd' 和 'b' 出现少于 3 次。保持它们的顺序,结果是 "dbb"。
示例 2:
输入:s = "xyz", k = 2
输出:"xyz"
解释:
所有字符('x','y','z')只出现一次,比 2 少。因此返回整个字符串。
提示:
1 <= s.length <= 100s只包含小写英文字母。1 <= k <= s.length
方法一:计数 · Go 实现
思考
保留原串中出现次数小于 的字符,顺序不变。先计数再过滤,避免一边删一边改频次。
得到全局次数后,第二次扫描把次数小于 的字符依次写入答案。
,两次线性扫描足够。
我们先遍历字符串 ,统计每个字符出现的频率,记录在哈希表或数组 中。
然后再遍历字符串 ,将出现次数少于 的字符添加到结果字符串中,最后返回结果字符串。
时间复杂度 ,其中 是字符串 的长度。空间复杂度 ,其中 是字符集大小。
func filterCharacters(s string, k int) string {
cnt := [26]int{}
for _, c := range s {
cnt[c-'a']++
}
ans := []rune{}
for _, c := range s {
if cnt[c-'a'] < k {
ans = append(ans, c)
}
}
return string(ans)
}
3664. 两个字母卡牌游戏 ​
题目、示例与约束
给你一副由字符串数组 cards 表示的牌,每张牌上都显示两个小写字母。
同时给你一个字母 x。你按照以下规则进行游戏:
- 从 0 分开始。
- 在每一轮中,你必须从牌堆中找到两张 兼容的 牌,这两张牌对应的字符串都包含字母
x。 - 移除这对牌并获得 1 分。
- 当你再也找不到兼容的牌对时,游戏结束。
返回在最优策略下你能获得的 最大 分数。
如果两张牌的字符串在 恰好 1 个位置上不同,则它们是兼容的。
示例 1:
输入: cards = ["aa","ab","ba","ac"], x = "a"
输出: 2
解释:
- 第一轮,选择并移除
"ab"和"ac",它们是兼容的,因为仅在下标 1 处不同。 - 第二轮,选择并移除
"aa"和"ba",它们是兼容的,因为仅在下标 0 处不同。
因为没有更多兼容的牌对,总分为 2。
示例 2:
输入: cards = ["aa","ab","ba"], x = "a"
输出: 1
解释:
- 第一轮,选择并移除
"aa"和"ba"。
因为没有更多兼容的牌对,总分为 1。
示例 3:
输入: cards = ["aa","ab","ba","ac"], x = "b"
输出: 0
解释:
唯一包含字符 'b' 的牌是 "ab" 和 "ba"。然而,它们在两个下标上都不同,所以它们不兼容。因此,输出为 0。
提示:
2 <= cards.length <= 105cards[i].length == 2- 每个
cards[i]仅由'a'到'j'之间的小写英文字母组成。 x是一个'a'到'j'之间的小写英文字母。
方法一:Go 实现(灵茶山艾府) · Go 实现
灵茶山艾府 · 源码 · MIT 许可 · 原题解
理解题意
注意字符串长度只有 。
题目要求所选字符串必须包含 ,所以我们能选的字符串有两组:
- 第一组:字符串的第一个字母是 ,即
- 第二组:字符串的第二个字母是 ,即
比如从第一组中选一个 和 ,这两个字符串恰好有一个下标上的字母不同,根据题意,它们是兼容的,可以消除,得分加一。
比如从第一组中选一个 ,从第二组中选一个 ,这两个字符串有两个下标上的字母不同,不符合要求,所以不能这么选。
看上去,第一组和第二组互相独立?然而字符串 在两组中都有。
没有 xx 的情况
从特殊到一般。首先思考,在没有 字符串的情况下,要怎么做。
在这种情况下,第一组和第二组是互相独立的,可以分别计算。
对于每一组,问题相当于:
- 给定数组 ,每次操作,删除 中的两个不同元素(配对)。最多可以操作多少次?
定理:最多操作 次。其中 是数组 的长度, 是出现次数最多的元素的出现次数。
有 xx 的情况
由于 的个数不超过 的长度,我们可以枚举分配多少个 给第一组,其余的 给第二组,然后用上面的公式计算答案,取最大值。
具体请看 视频讲解,欢迎点赞关注~
优化前
复杂度分析
- 时间复杂度:,其中 是 的长度, 是字符集合的大小。
- 空间复杂度:。
优化
可以不枚举 的个数,直接计算。
首先计算不考虑 时:
- 第一组的得分(配对个数),以及第一组配对后的剩余字符串个数 。
- 第二组的得分(配对个数),以及第二组配对后的剩余字符串个数 。
然后考虑 的影响。
首先,把 和剩余的字符串配对,每有 个 ,得分就能增加一,但这不能超过剩下的字符串个数 。
然后,如果还有 ,就撤销之前的配对,比如 改成 和 。这样每有 个 ,得分就能增加一,但这不能超过之前的配对个数 。由于这种方案平均每个 只能增加 分,不如上面的,所以先考虑把 和剩余的字符串配对,再考虑撤销之前的配对。
复杂度分析
- 时间复杂度:,其中 是 的长度, 是字符集合的大小。瓶颈在遍历 上。
- 空间复杂度:。
专题训练
见下面贪心与思维题单的「§1.8 相邻不同」。
分类题单
- 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
- 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
- 单调栈(基础/矩形面积/贡献法/最小字典序)
- 网格图(DFS/BFS/综合应用)
- 位运算(基础/性质/拆位/试填/恒等式/思维)
- 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
- 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
- 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
- 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
- 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
- 链表、二叉树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA/一般树)
- 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)
// https://space.bilibili.com/206214
// 计算这一组的得分(配对个数),以及剩余元素个数
func calc(cnt []int, x byte) (int, int) {
sum, mx := 0, 0
for i, c := range cnt {
if i != int(x-'a') {
sum += c
mx = max(mx, c)
}
}
pairs := min(sum/2, sum-mx)
return pairs, sum - pairs*2
}
func score(cards []string, x byte) int {
var cnt1, cnt2 [10]int
for _, s := range cards {
if s[0] == x {
cnt1[s[1]-'a']++
} else if s[1] == x {
cnt2[s[0]-'a']++
}
}
pairs1, left1 := calc(cnt1[:], x)
pairs2, left2 := calc(cnt2[:], x)
ans := pairs1 + pairs2 // 不考虑 xx 时的得分
cntXX := cnt1[x-'a']
// 把 xx 和剩下的 x? 和 ?x 配对
// 每有 1 个 xx,得分就能增加一,但这不能超过剩下的 x? 和 ?x 的个数 left1+left2
if cntXX > 0 {
mn := min(cntXX, left1+left2)
ans += mn
cntXX -= mn
}
// 如果还有 xx,就撤销之前的配对,比如 (ax,bx) 改成 (ax,xx) 和 (bx,xx)
// 每有 2 个 xx,得分就能增加一,但这不能超过之前的配对个数 pairs1+pairs2
// 由于这种方案平均每个 xx 只能增加 0.5 分,不如上面的,所以先考虑把 xx 和剩下的 x? 和 ?x 配对,再考虑撤销之前的配对
if cntXX > 0 {
ans += min(cntXX/2, pairs1+pairs2)
}
return ans
}
3668. 重排完成顺序 ​
题目、示例与约束
给你一个长度为 n 的整数数组 order 和一个整数数组 friends。
order包含从 1 到n的每个整数,且 恰好出现一次 ,表示比赛中参赛者按照 完成顺序 的 ID。friends包含你朋友们的 ID,按照 严格递增 的顺序排列。friends中的每个 ID 都保证出现在order数组中。
请返回一个数组,包含你朋友们的 ID,按照他们的 完成顺序 排列。
示例 1:
输入:order = [3,1,2,5,4], friends = [1,3,4]
输出:[3,1,4]
解释:
完成顺序是 [3, 1, 2, 5, 4]。因此,你朋友的完成顺序是 [3, 1, 4]。
示例 2:
输入:order = [1,4,5,3,2], friends = [2,5]
输出:[5,2]
解释:
完成顺序是 [1, 4, 5, 3, 2]。因此,你朋友的完成顺序是 [5, 2]。
提示:
1 <= n == order.length <= 100order包含从 1 到n的每个整数,且恰好出现一次1 <= friends.length <= min(8, n)1 <= friends[i] <= nfriends是严格递增的
方法一:自定义排序 · Go 实现
思考
是 的子序列,要按完赛顺序输出这些好友。 ,可建 ID 到名次的映射再排序。
字典 记录完赛名次,对 按 排序。
映射保证比较只依赖名次而非 ID 大小。
我们先根据 数组构建一个映射,记录每个 ID 的完成顺序。然后对 数组进行排序,排序的依据就是这些 ID 在 中的完成顺序。
时间复杂度 ,空间复杂度 。其中 是数组 的长度。
func recoverOrder(order []int, friends []int) []int {
n := len(order)
d := make([]int, n+1)
for i, x := range order {
d[x] = i
}
sort.Slice(friends, func(i, j int) bool {
return d[friends[i]] < d[friends[j]]
})
return friends
}
3678. 大于平均值的最小未出现正整数 ​
题目、示例与约束
给你一个整数数组 nums。
返回 nums 中 严格大于 nums 中所有元素 平均值 的 最小未出现正整数。
示例 1:
输入: nums = [3,5]
输出: 6
解释:
nums的平均值是(3 + 5) / 2 = 8 / 2 = 4。- 大于 4 的最小未出现正整数是 6。
示例 2:
输入: nums = [-1,1,2]
输出: 3
解释:
nums的平均值是(-1 + 1 + 2) / 3 = 2 / 3 = 0.667。- 大于 0.667 的最小未出现正整数是 3 。
示例 3:
输入: nums = [4,-1]
输出: 2
解释:
nums的平均值是(4 + (-1)) / 2 = 3 / 2 = 1.50。- 大于 1.50 的最小未出现正整数是 2。
提示:
1 <= nums.length <= 100-100 <= nums[i] <= 100
方法一:哈希表 · Go 实现
思考
要找不在数组中、且严格大于平均值的最小正整数。从 起逐个试探即可,因为缺失的正整数不会太远。
先把数组放进集合,平均值用整除得到下界。若候选已出现则加一,直到不在集合中。
最坏沿值域走一小段,仍与 同阶。
我们用一个哈希表 来记录数组 中出现过的元素。
然后,我们计算数组 的平均值 ,并将答案 初始化为 。
如果 在 中出现过,那么我们将 加一,直到 不在 中出现过为止。
最后,返回 即可。
时间复杂度 ,空间复杂度 ,其中 是数组 的长度。
func smallestAbsent(nums []int) int {
s := map[int]bool{}
sum := 0
for _, x := range nums {
s[x] = true
sum += x
}
ans := max(1, sum/len(nums)+1)
for s[ans] {
ans++
}
return ans
}
3682. 公共元素的最小索引和 🔒 ​
题目、示例与约束
给定两个整数数组 nums1 和 nums2,长度都为 n。
如果 nums1[i] == nums2[j],我们定义下标对 (i, j) 是 好数对。
返回所有可能的好数对中 i + j 的最小索引和。如果不存在这样的数对,则返回 -1。
示例 1:
输入:nums1 = [3,2,1], nums2 = [1,3,1]
输出:1
解释:
nums1和nums2之间的公共元素是 1 和 3。- 对于 3,
[i, j] = [0, 1],得到下标和是i + j = 1。 - 对于 1,
[i, j] = [2, 0],得到下标和是i + j = 2。 - 最小下标和是 1。
示例 2:
输入:nums1 = [5,1,2], nums2 = [2,1,3]
输出:2
解释:
nums1和nums2之间的公共元素是 1 和 2。- 对于 1,
[i, j] = [1, 1],得到下标和是i + j = 2。 - 对于 2,
[i, j] = [2, 0],得到下标和是i + j = 2。 - 最小下标和是 2。
示例 3:
输入:nums1 = [6,4], nums2 = [7,8]
输出:-1
解释:
- 由于
nums1和nums2之间没有公共元素,输出为 -1。
提示:
1 <= nums1.length == nums2.length <= 105-105 <= nums1[i], nums2[i] <= 105
方法一:哈希表 · Go 实现
思考
公共元素的最小下标和。若对 的每个值在 里扫描, 不可行。
哈希表记下 中每个值的首次下标,再扫 用 更新最小和。
只存首次出现,保证 一侧下标最小。无公共元素则返回 。
我们初始化一个变量 为无穷大,表示当前的最小索引和,用一个哈希表 来存储数组 中每个元素第一次出现的索引。
然后我们遍历数组 ,对于每个元素 ,如果它在 中存在,我们就计算它的索引和 ,并更新 。
最后如果 仍然是无穷大,说明没有找到任何公共元素,返回 -1;否则返回 。
时间复杂度 ,空间复杂度 。其中 是数组的长度。
func minimumSum(nums1 []int, nums2 []int) int {
const inf = 1 << 30
d := make(map[int]int)
for i, x := range nums2 {
if _, ok := d[x]; !ok {
d[x] = i
}
}
ans := inf
for i, x := range nums1 {
if j, ok := d[x]; ok {
ans = min(ans, i + j)
}
}
if ans == inf {
return -1
}
return ans
}
3712. 出现次数能被 K 整除的元素总和 ​
题目、示例与约束
给你一个整数数组 nums 和一个整数 k。
请返回一个整数,表示 nums 中所有其 出现次数 能被 k 整除的元素的总和;如果没有这样的元素,则返回 0 。
注意: 若某个元素在数组中的总出现次数能被 k 整除,则它在求和中会被计算 恰好 与其出现次数相同的次数。
元素 x 的 出现次数 指它在数组中出现的次数。
示例 1:
输入: nums = [1,2,2,3,3,3,3,4], k = 2
输出: 16
解释:
- 数字 1 出现 1 次(奇数次)。
- 数字 2 出现 2 次(偶数次)。
- 数字 3 出现 4 次(偶数次)。
- 数字 4 出现 1 次(奇数次)。
因此总和为 2 + 2 + 3 + 3 + 3 + 3 = 16。
示例 2:
输入: nums = [1,2,3,4,5], k = 2
输出: 0
解释:
没有元素出现偶数次,因此总和为 0。
示例 3:
输入: nums = [4,4,4,1,2,3], k = 3
输出: 12
解释:
- 数字 1 出现 1 次。
- 数字 2 出现 1 次。
- 数字 3 出现 1 次。
- 数字 4 出现 3 次。
因此总和为 4 + 4 + 4 = 12。
提示:
1 <= nums.length <= 1001 <= nums[i] <= 1001 <= k <= 100
方法一:计数 · Go 实现
思考
判定只看每个值的出现次数是否被 整除,与位置无关。 ,先计数再筛即可;满足条件的值按其出现次数计入总和。
我们用一个哈希表 来记录每个数字的出现次数。遍历数组 ,对于每个数字 ,我们将 增加 。
然后,我们遍历哈希表 ,对于每个元素 ,如果它的出现次数 能被 整除,就将 乘以它的出现次数加到结果中。
时间复杂度为 ,其中 是数组的长度。空间复杂度为 ,其中 是数组中不同元素的数量。
func sumDivisibleByK(nums []int, k int) (ans int) {
cnt := map[int]int{}
for _, x := range nums {
cnt[x]++
}
for x, v := range cnt {
if v%k == 0 {
ans += x * v
}
}
return
}
3713. 最长的平衡子串 I ​
题目、示例与约束
给你一个由小写英文字母组成的字符串 s。
如果一个 子串 中所有 不同 字符出现的次数都 相同 ,则称该子串为 平衡 子串。
请返回 s 的 最长平衡子串 的 长度 。
子串 是字符串中连续的、非空 的字符序列。
示例 1:
输入: s = "abbac"
输出: 4
解释:
最长的平衡子串是 "abba",因为不同字符 'a' 和 'b' 都恰好出现了 2 次。
示例 2:
输入: s = "zzabccy"
输出: 4
解释:
最长的平衡子串是 "zabc",因为不同字符 'z'、'a'、'b' 和 'c' 都恰好出现了 1 次。
示例 3:
输入: s = "aba"
输出: 2
解释:
最长的平衡子串之一是 "ab",因为不同字符 'a' 和 'b' 都恰好出现了 1 次。另一个最长的平衡子串是 "ba"。
提示:
1 <= s.length <= 1000s仅由小写英文字母组成。
方法一:枚举 + 计数 · Go 实现
思考
,枚举所有子串可行。平衡意味着每种出现过的字符次数相同,即「最大频次 种类数 长度」。固定左端点向右扫描并维护频次与种类数,即可在 内更新答案。
我们可以在 范围内枚举子串的起始位置 ,然后在 范围内枚举子串的结束位置 ,并使用哈希表 记录子串 中每个字符出现的次数。我们使用变量 记录子串中出现次数最多的字符的出现次数,使用变量 记录子串中不同字符的个数。如果在某个位置 ,满足 ,则说明子串 是一个平衡子串,我们更新答案 。
时间复杂度 ,其中 是字符串的长度。空间复杂度 ,其中 是字符集的大小,本题中 。
func longestBalanced(s string) (ans int) {
n := len(s)
for i := 0; i < n; i++ {
cnt := [26]int{}
mx, v := 0, 0
for j := i; j < n; j++ {
c := s[j] - 'a'
cnt[c]++
if cnt[c] == 1 {
v++
}
mx = max(mx, cnt[c])
if mx*v == j-i+1 {
ans = max(ans, j-i+1)
}
}
}
return ans
}
3718. 缺失的最小倍数 ​
题目、示例与约束
给你一个整数数组 nums 和一个整数 k,请返回从 nums 中缺失的、最小的正整数 k 的倍数。
倍数 指能被 k 整除的任意正整数。
示例 1:
输入: nums = [8,2,3,4,6], k = 2
输出: 10
解释:
当 k = 2 时,其倍数为 2、4、6、8、10、12……,其中在 nums 中缺失的最小倍数是 10。
示例 2:
输入: nums = [1,4,7,10,15], k = 5
输出: 5
解释:
当 k = 5 时,其倍数为 5、10、15、20……,其中在 nums 中缺失的最小倍数是 5。
提示:
1 <= nums.length <= 1001 <= nums[i] <= 1001 <= k <= 100
方法一:哈希表 + 枚举 · Go 实现
思考
所求是最小的正倍数 中未出现者。 与 都不超过 ,先把数组放入集合,再从小到大枚举倍数即可。
我们先用一个哈希表 存储数组 中出现的数字。然后从 的第一个正倍数 开始,依次枚举每个正倍数,直到找到第一个不在哈希表 中出现的倍数,即为答案。
时间复杂度 ,空间复杂度 。其中 为数组 的长度。
func missingMultiple(nums []int, k int) int {
s := map[int]bool{}
for _, x := range nums {
s[x] = true
}
for i := 1; ; i++ {
if x := k * i; !s[x] {
return x
}
}
}
3731. 找出缺失的元素 ​
题目、示例与约束
给你一个整数数组 nums ,数组由若干 互不相同 的整数组成。
数组 nums 原本包含了某个范围内的 所有整数 。但现在,其中可能 缺失 部分整数。
该范围内的 最小 整数和 最大 整数仍然存在于 nums 中。
返回一个 有序 列表,包含该范围内缺失的所有整数,并 按从小到大排序。如果没有缺失的整数,返回一个 空 列表。
示例 1:
输入: nums = [1,4,2,5]
输出: [3]
解释:
最小整数为 1,最大整数为 5,因此完整的范围应为 [1,2,3,4,5]。其中只有 3 缺失。
示例 2:
输入: nums = [7,8,6,9]
输出: []
解释:
最小整数为 6,最大整数为 9,因此完整的范围为 [6,7,8,9]。所有整数均已存在,因此没有缺失的整数。
示例 3:
输入: nums = [5,1]
输出: [2,3,4]
解释:
最小整数为 1,最大整数为 5,因此完整的范围应为 [1,2,3,4,5]。缺失的整数为 2、3 和 4。
提示:
2 <= nums.length <= 1001 <= nums[i] <= 100
方法一:哈希表 · Go 实现
思考
范围由数组最小、最大值确定,缺失值即该闭区间中未出现者。 ,放入集合后扫描 即可按升序列出。
我们先找出数组 中的最小值和最大值,记为 和 。然后我们使用哈希表存储数组 中的所有元素。
接下来,我们遍历区间 ,对于每个整数 ,如果 不在哈希表中,我们就将其加入答案列表中。
时间复杂度 ,空间复杂度 ,其中 是数组 的长度。
func findMissingElements(nums []int) (ans []int) {
mn, mx := 100, 0
s := make(map[int]bool)
for _, x := range nums {
mn = min(mn, x)
mx = max(mx, x)
s[x] = true
}
for x := mn + 1; x < mx; x++ {
if !s[x] {
ans = append(ans, x)
}
}
return
}
3740. 三个相等元素之间的最小距离 I ​
题目、示例与约束
给你一个整数数组 nums。
如果满足 nums[i] == nums[j] == nums[k],且 (i, j, k) 是 3 个 不同 下标,那么三元组 (i, j, k) 被称为 有效三元组 。
有效三元组 的 距离 被定义为 abs(i - j) + abs(j - k) + abs(k - i),其中 abs(x) 表示 x 的 绝对值 。
返回一个整数,表示 有效三元组 的 最小 可能距离。如果不存在 有效三元组 ,返回 -1。
示例 1:
输入: nums = [1,2,1,1,3]
输出: 6
解释:
最小距离对应的有效三元组是 (0, 2, 3) 。
(0, 2, 3) 是一个有效三元组,因为 nums[0] == nums[2] == nums[3] == 1。它的距离为 abs(0 - 2) + abs(2 - 3) + abs(3 - 0) = 2 + 1 + 3 = 6。
示例 2:
输入: nums = [1,1,2,3,2,1,2]
输出: 8
解释:
最小距离对应的有效三元组是 (2, 4, 6) 。
(2, 4, 6) 是一个有效三元组,因为 nums[2] == nums[4] == nums[6] == 2。它的距离为 abs(2 - 4) + abs(4 - 6) + abs(6 - 2) = 2 + 2 + 4 = 8。
示例 3:
输入: nums = [1]
输出: -1
解释:
不存在有效三元组,因此答案为 -1。
提示:
1 <= n == nums.length <= 1001 <= nums[i] <= n
方法一:哈希表 · Go 实现
思考
三元组距离在下标有序时等于 ,因此同一值的最优三元组必是下标列表中相邻的三个。按值收集下标后,对每条长度至少为 的列表滑动窗口即可; 足够。
我们可以使用一个哈希表 来存储数组中每个数字对应的下标列表。遍历数组时,将每个数字的下标添加到哈希表中对应的列表中。定义一个变量 来存储答案,初始值设为无穷大 。
接下来,我们遍历哈希表中的每个下标列表。如果某个数字的下标列表长度大于等于 ,则说明存在有效三元组。要使得距离最小,我们可以选择该数字下标列表中相邻的三个下标 、 和 ,假设 ,则该三元组的距离为 。我们遍历该下表列表所有相邻的三个下标组合,计算距离并更新答案。
最终,如果答案仍然是初始值 ,则说明不存在有效三元组,返回 ;否则返回计算得到的最小距离。
时间复杂度 ,空间复杂度 ,其中 是数组的长度。
func minimumDistance(nums []int) int {
g := make(map[int][]int)
for i, x := range nums {
g[x] = append(g[x], i)
}
inf := 1 << 30
ans := inf
for _, ls := range g {
m := len(ls)
for h := 0; h < m-2; h++ {
i := ls[h]
k := ls[h+2]
t := (k - i) * 2
ans = min(ans, t)
}
}
if ans == inf {
return -1
}
return ans
}
3741. 三个相等元素之间的最小距离 II ​
题目、示例与约束
给你一个整数数组 nums。
如果满足 nums[i] == nums[j] == nums[k],且 (i, j, k) 是 3 个 不同 下标,那么三元组 (i, j, k) 被称为 有效三元组 。
有效三元组 的 距离 被定义为 abs(i - j) + abs(j - k) + abs(k - i),其中 abs(x) 表示 x 的 绝对值 。
返回一个整数,表示 有效三元组 的 最小 可能距离。如果不存在 有效三元组 ,返回 -1。
示例 1:
输入: nums = [1,2,1,1,3]
输出: 6
解释:
最小距离对应的有效三元组是 (0, 2, 3) 。
(0, 2, 3) 是一个有效三元组,因为 nums[0] == nums[2] == nums[3] == 1。它的距离为 abs(0 - 2) + abs(2 - 3) + abs(3 - 0) = 2 + 1 + 3 = 6。
示例 2:
输入: nums = [1,1,2,3,2,1,2]
输出: 8
解释:
最小距离对应的有效三元组是 (2, 4, 6) 。
(2, 4, 6) 是一个有效三元组,因为 nums[2] == nums[4] == nums[6] == 2。它的距离为 abs(2 - 4) + abs(4 - 6) + abs(6 - 2) = 2 + 2 + 4 = 8。
示例 3:
输入: nums = [1]
输出: -1
解释:
不存在有效三元组,因此答案为 -1。
提示:
1 <= n == nums.length <= 1051 <= nums[i] <= n
方法一:哈希表 · Go 实现
思考
与上一题同一公式,只是 到了 。相邻三项仍然最优,按值分组后线性扫描每条下标列表即可,不必枚举所有三元组。
我们可以使用一个哈希表 来存储数组中每个数字对应的下标列表。遍历数组时,将每个数字的下标添加到哈希表中对应的列表中。定义一个变量 来存储答案,初始值设为无穷大 。
接下来,我们遍历哈希表中的每个下标列表。如果某个数字的下标列表长度大于等于 ,则说明存在有效三元组。要使得距离最小,我们可以选择该数字下标列表中相邻的三个下标 、 和 ,假设 ,则该三元组的距离为 。我们遍历该下表列表所有相邻的三个下标组合,计算距离并更新答案。
最终,如果答案仍然是初始值 ,则说明不存在有效三元组,返回 ;否则返回计算得到的最小距离。
时间复杂度 ,空间复杂度 ,其中 是数组的长度。
func minimumDistance(nums []int) int {
g := make(map[int][]int)
for i, x := range nums {
g[x] = append(g[x], i)
}
inf := 1 << 30
ans := inf
for _, ls := range g {
m := len(ls)
for h := 0; h < m-2; h++ {
i := ls[h]
k := ls[h+2]
t := (k - i) * 2
ans = min(ans, t)
}
}
if ans == inf {
return -1
}
return ans
}
3760. 不同首字母的子字符串数目 ​
题目、示例与约束
给你一个由小写英文字母组成的字符串 s。
返回一个整数,表示可以将 s 划分为子字符串的最大数量,使得每个 子字符串 都以一个 不同 字符开头(即,任意两个子字符串的首字符不能相同)。
子字符串 是字符串中一个连续、非空字符序列。
示例 1:
输入: s = "abab"
输出: 2
解释:
- 可以将
"abab"划分为"a"和"bab"。 - 每个子字符串都以不同的字符开头,即
'a'和'b'。因此,答案是 2。
示例 2:
输入: s = "abcd"
输出: 4
解释:
- 可以将
"abcd"划分为"a"、"b"、"c"和"d"。 - 每个子字符串都以不同的字符开头。因此,答案是 4。
示例 3:
输入: s = "aaaa"
输出: 1
解释:
"aaaa"中的所有字符都是'a'。- 只有一个子字符串可以以
'a'开头。因此,答案是 1。
提示:
1 <= s.length <= 105s仅由小写英文字母组成。
方法一 · Go 实现
思考
每个子串要以不同字符开头,最多只能有 段,且每个出现过的字符至多当一次开头。任意出现过的字符都可以单独成段,因此答案等于字符串中不同字符的个数。
func maxDistinct(s string) (ans int) {
cnt := [26]int{}
for _, c := range s {
cnt[c-'a']++
if cnt[c-'a'] == 1 {
ans++
}
}
return
}
3773. 最大等长连续字符组 🔒 ​
题目、示例与约束
给定一个由小写英文字母组成的字符串 s。
s 中的一个 连续字符组 是一个由无法再扩展的 相同 字符组成的 子串。例如,"hello" 中的连续字符组是 "h","e","ll" 和 "o"。
你可以 选择 s 中 相同 长度的字符组。
返回一个整数,表示你可以在 s 中选择的最多连续字符组。
示例 1:
输入:s = "hello"
输出:3
解释:
s 中的连续字符组是 "h","e","ll" 和 "o"。你可以选择 "h","e" 和 "o" 因为它们有相同的长度 1。
示例 2:
输入:s = "aaabaaa"
输出:2
解释:
s 中的连续字符组是 "aaa","b" 和 "aaa"。你可以选择 "aaa" 和 "aaa" 因为它们有相同的长度 3。
提示:
1 <= s.length <= 105s只包含小写英文字母。
方法一:哈希表 · Go 实现
思考
只能选择同一长度的连续段,答案是出现次数最多的那段长度。一遍 统计各长度出现次数,取最大值。
我们可以用一个哈希表 来记录每个连续字符组长度出现的次数。遍历字符串 ,对于每个连续字符组,计算其长度 ,并将 加 。最后,答案即为 中的最大值。
时间复杂度 ,空间复杂度 ,其中 是字符串 的长度。
func maxSameLengthRuns(s string) (ans int) {
cnt := map[int]int{}
n := len(s)
for i := 0; i < n; {
j := i + 1
for j < n && s[j] == s[i] {
j++
}
m := j - i
cnt[m]++
ans = max(ans, cnt[m])
i = j
}
return
}
3779. 得到互不相同元素的最少操作次数 ​
题目、示例与约束
给你一个整数数组 nums。
在一次操作中,你需要移除当前数组的 前三个元素。如果剩余元素少于三个,则移除 所有 剩余元素。
重复此操作,直到数组为空或不包含任何重复元素为止。
返回一个整数,表示所需的操作次数。
示例 1:
输入: nums = [3,8,3,6,5,8]
输出: 1
解释:
在第一次操作中,我们移除前三个元素。剩余的元素 [6, 5, 8] 互不相同,因此停止。仅需要一次操作。
示例 2:
输入: nums = [2,2]
输出: 1
解释:
经过一次操作后,数组变为空,满足停止条件。
示例 3:
输入: nums = [4,3,5,1,2]
输出: 0
解释:
数组中的所有元素都是互不相同的,因此不需要任何操作。
提示:
1 <= nums.length <= 1051 <= nums[i] <= 105
方法一:哈希表 + 倒序遍历 · Go 实现
思考
每次删前三个,本质是从左边切掉若干长度为 的块。从右往左看,第一次出现重复时,左边(含该位置)必须全部删掉,操作次数为 。
我们可以倒序遍历数组 ,并使用哈希表 记录已经遍历过的元素。当遍历到元素 时,如果 已经在哈希表 中,那么说明我们需要移除 的所有元素,需要的操作次数为 。否则,我们将 加入哈希表 中,并继续遍历下一个元素。
遍历结束后,如果没有找到重复的元素,那么数组中的元素已经互不相同,不需要进行任何操作,答案为 。
时间复杂度 ,空间复杂度 。其中 为数组 的长度。
func minOperations(nums []int) int {
st := make(map[int]struct{})
for i := len(nums) - 1; i >= 0; i-- {
if _, ok := st[nums[i]]; ok {
return i/3 + 1
}
st[nums[i]] = struct{}{}
}
return 0
}
3784. 使所有字符相等的最小删除代价 ​
题目、示例与约束
给你一个长度为 n 的字符串 s 和一个整数数组 cost,其中 cost[i] 表示 删除 字符串 s 中第 i 个字符的代价。
你可以从字符串 s 中删除任意数量的字符(也可以不删除),使得最终的字符串 非空 且由 相同 字符组成。
返回实现上述目标所需的 最小 总删除代价。
示例 1:
输入: s = "aabaac", cost = [1,2,3,4,1,10]
输出: 11
解释:
删除索引为 0、1、2、3 和 4 的字符后,字符串变为 "c",它由相同的字符组成,总删除代价为:cost[0] + cost[1] + cost[2] + cost[3] + cost[4] = 1 + 2 + 3 + 4 + 1 = 11。
示例 2:
输入: s = "abc", cost = [10,5,8]
输出: 13
解释:
删除索引为 1 和 2 的字符后,字符串变为 "a",它由相同的字符组成,总删除代价为:cost[1] + cost[2] = 5 + 8 = 13。
示例 3:
输入: s = "zzzzz", cost = [67,67,67,67,67]
输出: 0
解释:
字符串 s 中的所有字符都相同,因此不需要删除字符,删除代价为 0。
提示:
n == s.length == cost.length1 <= n <= 1051 <= cost[i] <= 109s仅由小写英文字母组成。
方法一:分组 + 枚举 · Go 实现
思考
最终必须留下同一种字符且非空,等价于删光其余字符。每种字符的保留代价是总删除代价减去该字符自身代价和,取最小者。
我们计算字符串中每个字符的删除代价总和,并将其存储在哈希表 中,其中键为字符,值为对应的删除代价总和。我们还计算删除所有字符的总代价 。
接下来,我们遍历哈希表 ,对于每个字符 ,计算保留该字符所需的最小删除代价,即 。最终答案为所有字符对应的最小删除代价中的最小值。
时间复杂度 ,空间复杂度 ,其中 是字符串 的长度,而 是字符串中不同字符的集合。
func minCost(s string, cost []int) int64 {
tot := int64(0)
g := map[byte]int64{}
for i, v := range cost {
tot += int64(v)
g[s[i]] += int64(v)
}
ans := tot
for _, x := range g {
ans = min(ans, tot-x)
}
return ans
}
3803. 统计残差前缀 ​
题目、示例与约束
给你一个仅由小写英文字母组成的字符串 s。
如果字符串 s 的某个 前缀 中 不同字符的数量 等于 len(prefix) % 3,则该前缀被称为残差前缀(residue)。
返回字符串 s 中 残差前缀 的数量。
字符串的 前缀 是一个 非空子字符串,从字符串的开头起始并延伸到任意位置。
示例 1:
输入: s = "abc"
输出: 2
解释:
- 前缀
"a"有 1 个不同字符,且长度模 3 为 1,因此它是一个残差前缀。 - 前缀
"ab"有 2 个不同字符,且长度模 3 为 2,因此它是一个残差前缀。 - 前缀
"abc"不满足条件,因此不是残差前缀。
因此,答案是 2。
示例 2:
输入: s = "dd"
输出: 1
解释:
- 前缀
"d"有 1 个不同字符,且长度模 3 为 1,因此它是一个残差前缀。 - 前缀
"dd"有 1 个不同字符,但长度模 3 为 2,因此它不是残差前缀。
因此,答案是 1。
示例 3:
输入: s = "bob"
输出: 2
解释:
- 前缀
"b"有 1 个不同字符,且长度模 3 为 1,因此它是一个残差前缀。 - 前缀
"bo"有 2 个不同字符,且长度模 3 为 2,因此它是一个残差前缀。 - 前缀
"bob"不满足条件。
因此,答案是 2。
提示:
1 <= s.length <= 100s仅包含小写英文字母。
方法一:哈希表 · Go 实现
思考
每个前缀都需判断不同字符数是否等于长度模 。 ,枚举全部前缀并各扫一遍可通过,但会重复统计同一前缀内的字符。
前缀是嵌套的:第 个前缀只比前一个多一个字符。不同字符集合单调不减。
因此维护一个集合记录已出现字符,每读入一位更新集合大小,再与 比较即可。
一次从左到右的遍历给出全部残差前缀个数。
我们用一个哈希表 来记录当前前缀中出现过的不同字符的集合。遍历字符串 的每个字符 ,将其加入集合 中,然后判断当前前缀的长度对 取模的结果是否等于集合 的大小。如果相等,则说明当前前缀是一个残差前缀,答案加 。
遍历结束后,返回答案即可。
时间复杂度 ,空间复杂度 。其中 是字符串 的长度。
func residuePrefixes(s string) int {
st := make(map[rune]struct{})
ans := 0
for i, c := range s {
idx := i + 1
st[c] = struct{}{}
if len(st) == idx%3 {
ans++
}
}
return ans
}
3804. 中心子数组的数量 ​
题目、示例与约束
给你一个整数数组 nums。
如果一个 子数组 的元素之和 等于 该子数组中的 至少一个元素,则该子数组被称为 中心子数组。
返回数组 nums 中 中心子数组 的数量。
子数组 是数组中的一个连续、非空元素序列。
示例 1:
输入: nums = [-1,1,0]
输出: 5
解释:
- 所有单元素子数组(
[-1],[1],[0])都是中心子数组。 - 子数组
[1, 0]的元素之和为 1,且 1 存在于该子数组中。 - 子数组
[-1, 1, 0]的元素之和为 0,且 0 存在于该子数组中。 - 因此,答案是 5。
示例 2:
输入: nums = [2,-3]
输出: 2
解释:
只有单元素子数组([2],[-3])是中心子数组。
提示:
1 <= nums.length <= 500-105 <= nums[i] <= 105
方法一:哈希表 + 枚举 · Go 实现
思考
中心子数组要求区间和等于区间内某个元素。 ,三重循环枚举左右端点再查找元素仍偏重。
固定左端点后向右延伸时,区间和与元素集合均可增量维护。
为此枚举起点 ,用累加和 与哈希集合记录 中的值,若 落在集合中则计数。
枚举全部子数组,哈希判断将内层查找降为均摊常数。
我们枚举所有子数组的起始下标 ,然后从下标 开始枚举子数组的结束下标 ,计算子数组 的元素之和 ,并将子数组中的所有元素加入哈希表 中。每次枚举结束后,判断 是否在哈希表 中出现过,如果出现过,则说明子数组 是一个中心子数组,答案加 。
时间复杂度 ,空间复杂度 。其中 是数组 的长度。
func centeredSubarrays(nums []int) int {
n := len(nums)
ans := 0
for i := 0; i < n; i++ {
st := make(map[int]struct{})
s := 0
for j := i; j < n; j++ {
s += nums[j]
st[nums[j]] = struct{}{}
if _, ok := st[s]; ok {
ans++
}
}
}
return ans
}
3837. 相等元素的延迟计数 🔒 ​
题目、示例与约束
给定一个长度为 n 的整数数组 nums 和一个整数 k。
对于每个下标 i,将 延迟计数 定义为满足以下条件的索引 j 的数量:
i + k < j <= n - 1,且nums[j] == nums[i]
返回一个数组 ans,其中 ans[i] 是下标 i 的 延迟计数。
示例 1:
输入:nums = [1,2,1,1], k = 1
输出:[2,0,0,0]
解释:
i |
nums[i] |
可能的 j |
nums[j] |
满足nums[j] == nums[i] |
ans[i] |
|---|---|---|---|---|---|
| 0 | 1 | [2, 3] | [1, 1] | [2, 3] | 2 |
| 1 | 2 | [3] | [1] | [] | 0 |
| 2 | 1 | [] | [] | [] | 0 |
| 3 | 1 | [] | [] | [] | 0 |
因此,ans = [2, 0, 0, 0]。
示例 2:
输入:nums = [3,1,3,1], k = 0
输出:[1,1,0,0]
解释:
i |
nums[i] |
可能的 j |
nums[j] |
满足nums[j] == nums[i] |
ans[i] |
|---|---|---|---|---|---|
| 0 | 3 | [1, 2, 3] | [1, 3, 1] | [2] | 1 |
| 1 | 1 | [2, 3] | [3, 1] | [3] | 1 |
| 2 | 3 | [3] | [1] | [] | 0 |
| 3 | 1 | [] | [] | [] | 0 |
因此,ans = [1, 1, 0, 0].
提示:
1 <= n == nums.length <= 1051 <= nums[i] <= 1050 <= k <= n - 1
方法一:哈希表 + 倒序枚举 · Go 实现
思考
统计满足 且 的个数。 ,对每个 向右扫描不可行。
窗口 随 减小而扩大,新进入的是下标 。
自 倒序枚举 ,先把 计入哈希表,再读取 的出现次数。
每个下标恰好加入一次,线性完成全部查询。
我们可以使用一个哈希表 来记录每个数字在索引范围 内出现的次数。我们从下标 开始倒序枚举索引 ,在枚举的过程中先将索引 处的数字加入哈希表 中,然后将 的值赋给答案数组 。
时间复杂度 ,空间复杂度 。其中 为数组 的长度。
func delayedCount(nums []int, k int) []int {
n := len(nums)
cnt := map[int]int{}
ans := make([]int, n)
for i := n - k - 2; i >= 0; i-- {
cnt[nums[i+k+1]]++
ans[i] = cnt[nums[i]]
}
return ans
}
3839. 前缀连接组的数目 ​
题目、示例与约束
给你一个字符串数组 words 和一个整数 k。
如果两个位于 不同下标 的单词 a 和 b 满足 a[0..k-1] == b[0..k-1],则称它们是 前缀连接的。
一个 连接组 是指一组单词,其中每对单词都是前缀连接的。
返回从给定的单词中形成包含 至少 两个单词的 连接组数目 。
注意:
- 长度小于
k的单词不能加入任何组,应被忽略。 - 重复的字符串被视为不同的单词。
- 字符串的 前缀 是指从字符串开头开始并延伸到其中任意位置的子字符串。
示例 1:
输入: words = ["apple","apply","banana","bandit"], k = 2
输出: 2
解释:
共享相同前 k = 2 个字母的单词被分为一组:
words[0] = "apple"和words[1] = "apply"共享前缀"ap"。words[2] = "banana"和words[3] = "bandit"共享前缀"ba"。
因此,共有 2 个连接组,每个组至少包含两个单词。
示例 2:
输入: words = ["car","cat","cartoon"], k = 3
输出: 1
解释:
根据长度为 k = 3 的前缀对单词进行评估:
words[0] = "car"和words[2] = "cartoon"共享前缀"car"。words[1] = "cat"不与任何其他单词共享长度为 3 的前缀。
因此,共有 1 个连接组。
示例 3:
输入: words = ["bat","dog","dog","doggy","bat"], k = 3
输出: 2
解释:
根据长度为 k = 3 的前缀对单词进行评估:
words[0] = "bat"和words[4] = "bat"形成一个组。words[1] = "dog",words[2] = "dog"和words[3] = "doggy"共享前缀"dog"。
因此,共有 2 个连接组,每个组至少包含两个单词。
提示:
1 <= words.length <= 50001 <= words[i].length <= 1001 <= k <= 100words中的所有字符串均由小写英文字母组成。
方法一:哈希表 · Go 实现
思考
长度至少 且前 个字符相同的单词构成一组,统计至少两人的组数。 ,两两比较前缀偏慢且不必要。
一组完全由长度为 的前缀字符串决定,与其余后缀无关。
对长度 的单词把 计入哈希表,再数出现次数大于 的键。
短于 的单词直接忽略。
我们用一个哈希表 来统计每个长度大于等于 的字符串的前 个字符组成的前缀出现的次数。最后统计 中值大于 的键的数量,即为连接组的数目。
时间复杂度 ,空间复杂度 。其中 是 的长度。
func prefixConnected(words []string, k int) int {
cnt := make(map[string]int)
for _, w := range words {
if len(w) >= k {
cnt[w[:k]]++
}
}
ans := 0
for _, v := range cnt {
if v > 1 {
ans++
}
}
return ans
}
3842. 切换打开灯泡 ​
题目、示例与约束
给你一个整数数组 bulbs,其中每个元素的取值范围为 1 到 100。
有 100 个电灯泡,按从 1 到 100 编号,初始时所有灯泡均为关闭状态。
对于数组 bulbs 中的每一个元素 bulbs[i],执行以下操作:
- 如果第
bulbs[i]个灯泡当前是关闭状态,将其打开。 - 如果第
bulbs[i]个灯泡当前是打开状态,将其关闭。
返回一个整数列表,表示最终处于打开状态的灯泡编号,按升序排列。如果没有灯泡是打开的,返回一个空列表。
示例 1:
输入: bulbs = [10,30,20,10]
输出: [20,30]
解释:
- 第
bulbs[0] = 10个灯泡当前是关闭状态,将其打开。 - 第
bulbs[1] = 30个灯泡当前是关闭状态,将其打开。 - 第
bulbs[2] = 20个灯泡当前是关闭状态,将其打开。 - 第
bulbs[3] = 10个灯泡当前是打开状态,将其关闭。 - 最终,第 20 个和第 30 个灯泡处于打开状态。
示例 2:
输入: bulbs = [100,100]
输出: []
解释:
- 第
bulbs[0] = 100个灯泡当前是关闭状态,将其打开。 - 第
bulbs[1] = 100个灯泡当前是打开状态,将其关闭。 - 最终,没有灯泡处于打开状态。
提示:
1 <= bulbs.length <= 1001 <= bulbs[i] <= 100
方法一:模拟 · Go 实现
思考
灯泡编号 到 ,每次遇到编号则开关翻转。数组长度 ,直接模拟。
最终状态只取决于每个编号出现次数的奇偶。
用长 的数组异或 记录翻转,最后收集仍为 的下标。
下标顺序即升序,无需再排序。
我们用一个长度为 的数组 来记录每个灯泡的状态,初始时所有元素为 ,表示所有灯泡均为关闭状态。对于数组 中的每一个元素 ,我们将 的值取反(即 变为 , 变为 )。最后,我们遍历 数组,将值为 的索引加入结果列表中,并返回结果。
时间复杂度 ,其中 是数组 的长度。空间复杂度 ,其中 是灯泡的最大编号。
func toggleLightBulbs(bulbs []int) []int {
st := make([]int, 101)
for _, x := range bulbs {
st[x] ^= 1
}
ans := make([]int, 0)
for i := 0; i < 101; i++ {
if st[i] == 1 {
ans = append(ans, i)
}
}
return ans
}
3843. 频率唯一的第一个元素 ​
题目、示例与约束
给你一个整数数组 nums。
返回数组中第一个(从左到右扫描)出现频率与众不同 的元素。如果不存在这样的元素,返回 -1。
示例 1:
输入: nums = [20,10,30,30]
输出: 30
解释:
- 20 出现了 1 次。
- 10 出现了 1 次。
- 30 出现了 2 次。
- 30 的出现频率是唯一的,因为没有其他整数恰好出现 2 次。
示例 2:
输入: nums = [20,20,10,30,30,30]
输出: 20
解释:
- 20 出现了 2 次。
- 10 出现了 1 次。
- 30 出现了 3 次。
- 20、10 和 30 的出现频率各不相同。第一个出现频率唯一的元素是 20。
示例 3:
输入: nums = [10,10,20,20]
输出: -1
解释:
- 10 出现了 2 次。
- 20 出现了 2 次。
- 没有任何元素的出现频率是唯一的。
提示:
1 <= nums.length <= 1051 <= nums[i] <= 105
方法一:哈希表 · Go 实现
思考
求从左到右第一个「其出现次数在全局也只出现一次」的元素。 ,需线性。
先要知道每个值的频次,再知道每个频次被多少个值占用。
两次计数:值到次数,次数到「有多少个值取该次数」。再按原序扫描,遇到次数的占用为 即返回。
若无则 。
我们用一个哈希表 来统计每个元素出现的次数,然后用另一个哈希表 来统计每个出现次数的频率。最后我们再次遍历数组 ,对于每个元素 ,如果 的值为 1,说明 的出现频率是唯一的,我们返回 。如果遍历结束后没有找到这样的元素,返回 -1。
时间复杂度 ,空间复杂度 。其中 是数组 的长度。
func firstUniqueFreq(nums []int) int {
cnt := make(map[int]int)
for _, x := range nums {
cnt[x]++
}
freq := make(map[int]int)
for _, v := range cnt {
freq[v]++
}
for _, x := range nums {
if freq[cnt[x]] == 1 {
return x
}
}
return -1
}
3846. 使用单指输入字符串的总距离 🔒 ​
题目、示例与约束
| q | w | e | r | t | y | u | i | o | p |
| a | s | d | f | g | h | j | k | l | |
| z | x | c | v | b | n | m |
给定一个只包含小写英文字母的字符串 s。返回一个整数,表示使用仅一个手指输入字符串 s 的总距离。手指初始位置在字母键 'a' 上。
两个位于 (r1, c1) 和 (r2, c2) 的按键距离是 |r1 - r2| + |c1 - c2|。
示例 1:
输入:s = "hello"
输出:17
解释:
- 你的手指从
'a'开始,位于(1, 0)。 - 移动到
'h',位于(1, 5)。距离是|1 - 1| + |0 - 5| = 5。 - 移动到
'e',位于(0, 2)。距离是|1 - 0| + |5 - 2| = 4。 - 移动到
'l',位于(1, 8)。距离是|0 - 1| + |2 - 8| = 7。 - 移动到
'l',位于(1, 8)。距离是|1 - 1| + |8 - 8| = 0。 - 移动到
'o',位于(0, 8)。距离是|1 - 0| + |8 - 8| = 1。 - 总距离是
5 + 4 + 7 + 0 + 1 = 17。
示例 2:
输入:s = "a"
输出:0
解释:
- 你的手指从
'a'开始,位于(1, 0)。 - 移动到
'a',位于(1, 0)。距离是|1 - 1| + |0 - 0| = 0。 - 总距离为 0。
提示:
1 <= s.length <= 104s只包含小写英文字母。
方法一:模拟 · Go 实现
思考
单指从 出发,按 的字母顺序移动,距离为网格曼哈顿距离。 ,按键布局固定。
相邻两次按键的距离只取决于两字母坐标,与更早历史无关。
预处理三行键盘坐标,沿 累加相邻曼哈顿距离,起点视为 。
一次扫描即总路程。
我们定义一个哈希表 ,用来存储每个字符在键盘上的位置。对于字符串 中的每个字符,我们计算从上一个字符到当前字符的距离,并将其累加到答案中。最后返回答案即可。
时间复杂度 ,其中 是字符串 的长度。空间复杂度 ,其中 是字符集,这里是 26 个小写英文字母。
var pos map[byte][2]int
func init() {
pos = make(map[byte][2]int)
keys := []string{"qwertyuiop", "asdfghjkl", "zxcvbnm"}
for i, row := range keys {
for j := 0; j < len(row); j++ {
pos[row[j]] = [2]int{i, j}
}
}
}
func totalDistance(s string) int {
pre := byte('a')
ans := 0
for i := 0; i < len(s); i++ {
cur := s[i]
p1 := pos[pre]
p2 := pos[cur]
ans += abs(p1[0]-p2[0]) + abs(p1[1]-p2[1])
pre = cur
}
return ans
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
3852. 不同频率的最小数对 ​
题目、示例与约束
给你一个整数数组 nums。
从 nums 中找出两个 互不相同 的值 x 和 y,使得:
x < yx和y在nums中的频率不同。
在所有满足条件的数对中:
- 选择
x的值尽可能小的数对。 - 如果存在多个
x相同的数对,选择y的值尽可能小的那个。
返回一个整数数组 [x, y]。如果不存在有效的数对,返回 [-1, -1]。
一个值 x 的 频率 是指它在数组中出现的次数。
示例 1:
输入: nums = [1,1,2,2,3,4]
输出: [1,3]
解释:
最小的值是 1,频率为 2。比 1 大且频率与 1 不同的最小值是 3,其频率为 1。因此,答案是 [1, 3]。
示例 2:
输入: nums = [1,5]
输出: [-1,-1]
解释:
两个值的频率相同,因此不存在有效的数对。返回 [-1, -1]。
示例 3:
输入: nums = [7]
输出: [-1,-1]
解释:
数组中只有一个值,因此不存在有效的数对。返回 [-1, -1]。
提示:
1 <= nums.length <= 1001 <= nums[i] <= 100
方法一:哈希表 · Go 实现
思考
找 且频次不同的数对,先最小 再最小 。值域与长度均 。
必须是数组中的最小值,因为还要求 尽量小。
统计频次后取最小键为 ,再在其余键中找频次不同且值最小的 。
若无则 。
我们用一个哈希表 来统计每个值在数组中的频率。然后我们找到最小的值 ,以及比 大且频率与 不同的最小值 。如果不存在这样的 ,则返回 。
时间复杂度 ,空间复杂度 ,其中 是数组 的长度。
func minDistinctFreqPair(nums []int) []int {
const inf = math.MaxInt
cnt := make(map[int]int)
for _, v := range nums {
cnt[v]++
}
x := slices.Min(nums)
minY := inf
for y := range cnt {
if y < minY && cnt[x] != cnt[y] {
minY = y
}
}
if minY == inf {
return []int{-1, -1}
}
return []int{x, minY}
}
3853. 合并靠近字符 ​
题目、示例与约束
给你一个由小写英文字母组成的字符串 s 和一个整数 k。
在 当前 字符串 s 中,如果两个 相同 字符之间的下标距离 至多 为 k,则认为它们是 靠近 的。
当两个字符 靠近 时,右侧的字符会合并到左侧。合并操作 逐个 发生,每次合并后,字符串都会更新,直到无法再进行合并为止。
返回执行所有可能合并后的最终字符串。
注意:如果可以进行多次合并,请始终选择 左侧下标最小 的那一对进行合并。如果多对字符共享最小的左侧下标,请选择 右侧下标最小 的那一对。
示例 1:
输入: s = "abca", k = 3
输出: "abc"
解释:
- 下标
i = 0和i = 3处的字符'a'是靠近的,因为3 - 0 = 3 <= k。 - 将它们合并到左侧的
'a',得到s = "abc"。 - 没有其他相同的字符是靠近的,因此不再发生合并。
示例 2:
输入: s = "aabca", k = 2
输出: "abca"
解释:
- 下标
i = 0和i = 1处的字符'a'是靠近的,因为1 - 0 = 1 <= k。 - 将它们合并到左侧的
'a',得到s = "abca"。 - 现在剩余的字符
'a'分别位于下标i = 0和i = 3,它们不再靠近,因为k < 3,所以不再发生合并。
示例 3:
输入: s = "yybyzybz", k = 2
输出: "ybzybz"
解释:
- 下标
i = 0和i = 1处的字符'y'是靠近的,因为1 - 0 = 1 <= k。 - 将它们合并到左侧的
'y',得到s = "ybyzybz"。 - 现在下标
i = 0和i = 2处的字符'y'是靠近的,因为2 - 0 = 2 <= k。 - 将它们合并到左侧的
'y',得到s = "ybzybz"。 - 没有其他相同的字符是靠近的,因此不再发生合并。
提示:
1 <= s.length <= 1001 <= k <= s.lengths由小写英文字母组成。
方法一:哈希表 · Go 实现
思考
相同字符若在当前串中下标距离 则右侧并入左侧,且总是合并最左一对。 ,但按题意模拟每次改写字符串易乱。
合并等价于:新字符若与答案中上一次该字符的位置距离不超过 ,则被吸收而不追加。
用哈希表记录每个字符在答案中的最近下标,遍历原串:能合并则跳过,否则追加并更新位置。
因总是优先最左,从左构造答案与反复选最左对一致。
我们使用一个哈希表 来记录每个字符上一次出现的位置。我们遍历字符串中的每个字符,如果当前字符之前出现过,并且当前下标与上一次出现的下标之差不超过 ,则跳过该字符;否则,将该字符添加到答案中,并更新其在哈希表中的位置。
时间复杂度 ,空间复杂度 ,其中 是字符串的长度,而 是字符集的大小,本题中字符集为小写英文字母,因此 是常数。
func mergeCharacters(s string, k int) string {
last := make(map[byte]int)
var ans []byte
for i := 0; i < len(s); i++ {
c := s[i]
cur := len(ans)
if lastIdx, ok := last[c]; ok && cur-lastIdx <= k {
continue
}
ans = append(ans, c)
last[c] = cur
}
return string(ans)
}
3860. 不同邮件组 🔒 ​
题目、示例与约束
给定一个字符串数组 emails,其中每个字符串是一个有效的邮件地址。
如果两个邮件地址的 规范化 本地名称和 规范化 域名名称 都相同,则属于同一组。
规范化规则如下:
- 本地名称是
'@'符号 之前 的部分。- 忽略所有点
'.'。 - 忽略第一个
'+'之后的所有内容,如果存在的话。 - 转换为小写。
- 忽略所有点
- 域名是
'@'符号 后面 的部分。- 转换为小写。
返回一个整数,表示规范化后的 不同 电子邮件组的数量。
示例 1:
输入:emails = ["test.email+alex@leetcode.com", "test.e.mail+bob.cathy@leetcode.com", "testemail+david@lee.tcode.com"]
输出:2
示例:
| 邮件地址 | 本地名称 | 规范化本地名称 | 域名 | 规范化域名 | 最终邮件地址 |
|---|---|---|---|---|---|
| test.email+alex@leetcode.com | test.email+alex | testemail | leetcode.com | leetcode.com | testemail@leetcode.com |
| test.e.mail+bob.cathy@leetcode.com | test.e.mail+bob.cathy | testemail | leetcode.com | leetcode.com | testemail@leetcode.com |
| testemail+david@lee.tcode.com | testemail+david | testemail | lee.tcode.com | lee.tcode.com | testemail@lee.tcode.com |
不同的邮件地址是 ["testemail@leetcode.com", "testemail@lee.tcode.com"]。因此,答案是 2。
示例 2:
输入:emails = ["A@B.com", "a@b.com", "ab+xy@b.com", "a.b@b.com"]
输出:2
示例:
| 邮件地址 | 本地名称 | 规范化本地名称 | 域名 | 规范化域名 | 最终邮件地址 |
|---|---|---|---|---|---|
| A@B.com | A | a | B.com | b.com | a@b.com |
| a@b.com | a | a | b.com | b.com | a@b.com |
| ab+xy@b.com | ab+xy | ab | b.com | b.com | ab@b.com |
| a.b@b.com | a.b | ab | b.com | b.com | ab@b.com |
不同的邮件地址是 ["a@b.com", "ab@b.com"]。因此,答案是 2。
示例 3:
输入:emails = ["a.b+c.d+e@DoMain.com", "ab+xyz@domain.com", "ab@domain.com"]
输出:1
解释:
| 邮件地址 | 本地名称 | 规范化本地名称 | 域名 | 规范化域名 | 最终邮件地址 |
|---|---|---|---|---|---|
| a.b+c.d+e@DoMain.com | a.b+c.d+e | ab | DoMain.com | domain.com | ab@domain.com |
| ab+xyz@domain.com | ab+xyz | ab | domain.com | domain.com | ab@domain.com |
| ab@domain.com | ab | ab | domain.com | domain.com | ab@domain.com |
所有邮件地址规范化为 "ab@domain.com"。因此,答案是 1。
提示:
1 <= emails.length <= 10001 <= emails[i].length <= 100emails[i]包含大小写英文字母,数字,以及字符'.','+'和'@'。- 每个
emails[i]包含 恰好 一个'@'字符。 - 所有本地名称和域名都不为空;本地名不能以
'+'开头。 - 域名以
".com"后缀结尾,并且在".com"之前至少包含一个字符。
方法一:哈希表 · Go 实现
思考
规范化后相同的邮箱算一组,求组数。邮箱至多 个,按规则变换后投入集合即可。
本地名去点、截断首个 之后,再与域名一并转小写。
集合去重后的大小即不同组数。
每个地址处理一次,与变换规则一一对应。
我们可以使用一个哈希表 来存储每个邮箱地址的规范化结果。对于每个邮箱地址,我们按照题目要求进行规范化处理:
- 将邮箱地址分为本地名和域名两部分。
- 对于本地名,去掉所有的点
.,并且如果存在加号+,则去掉加号及其后面的部分。最后将本地名转换为小写。 - 对于域名,将其转换为小写。
- 将规范化后的本地名、@ 分隔符和域名拼接起来,得到规范化后的邮箱地址,并将其加入哈希表 中。
最后,哈希表 中的元素个数即为唯一邮箱组的数量。
时间复杂度 ,其中 和 分别是邮箱地址的数量和每个邮箱地址的平均长度。空间复杂度 ,最坏情况下所有邮箱地址都不同。
func uniqueEmailGroups(emails []string) int {
st := make(map[string]struct{})
for _, email := range emails {
parts := strings.Split(email, "@")
local := parts[0]
domain := parts[1]
if idx := strings.Index(local, "+"); idx != -1 {
local = local[:idx]
}
local = strings.ReplaceAll(local, ".", "")
local = strings.ToLower(local)
domain = strings.ToLower(domain)
normalized := local + "@" + domain
st[normalized] = struct{}{}
}
return len(st)
}
3866. 找到第一个唯一偶数 ​
题目、示例与约束
给你一个整数数组 nums。
请你返回一个整数,表示 nums 中出现 恰好 一次的第一个 偶数(以数组下标最早为准)。如果不存在这样的整数,返回 -1。
如果一个整数 x 能被 2 整除,那么它就被认为是 偶数。
示例 1:
输入: nums = [3,4,2,5,4,6]
输出: 2
解释:
2 和 6 都是偶数,并且它们都恰好出现一次。因为 2 在数组中出现得更早,所以答案是 2。
示例 2:
输入: nums = [4,4]
输出: -1
解释:
没有恰好出现一次的偶数,所以返回 -1。
提示:
1 <= nums.length <= 1001 <= nums[i] <= 100
方法一:计数 · Go 实现
思考
求第一次出现且全局只出现一次的偶数。长度 ,两次遍历。
先计数再按原序扫描,保证「下标最早」。
条件为偶且频次为 。
没有则 。
我们可以用一个哈希表或数组 来统计每个整数在数组中出现的次数。然后我们再遍历一次数组,找到第一个满足条件的偶数并返回它。如果没有满足条件的偶数,我们返回 -1。
时间复杂度 ,其中 是数组的长度。空间复杂度 ,其中 是数组中整数的范围(在本题中为 100)。
func firstUniqueEven(nums []int) int {
cnt := make([]int, 101)
for _, x := range nums {
cnt[x]++
}
for _, x := range nums {
if x%2 == 0 && cnt[x] == 1 {
return x
}
}
return -1
}
继续阅读
- 实践案例
- 实践案例
- 实践案例