必须拿起的最小连续卡牌数-LeetCode
2260. 必须拿起的最小连续卡牌数
🎯 问题描述(来源于LeetCode)
描述:
给你一个整数数组 cards,其中 cards[i] 表示第 i 张卡牌的值。
如果两张卡牌的值相同,则认为这一对卡牌是匹配的。
请你找出必须拿起的最小连续卡牌数目,使得其中包含一对匹配的卡牌。
如果无法找到匹配的一对卡牌,返回 -1。
说明:
-
这是一个哈希表结合贪心算法的问题。
-
核心逻辑是:对于每个数字,记录它最后一次出现的位置。
-
当再次遇到该数字时,计算当前位置与上次出现位置之间的距离,并更新最小距离。
-
通过一次遍历即可完成查找。
-
示例:
-
示例 1:
1 | 输入:cards = [3,4,2,3,4,7] |
- 示例 2:
1 | 输入:cards = [1,0,5,3] |
💻 解题思路
思路1:哈希表记录最后出现位置
思路1:代码实现
1 | class Solution: |
思路1:📊 性能分析
- 时间复杂度:
- 其中 是数组
cards的长度。 - 我们只遍历数组一次,每次哈希表操作(查找、插入、更新)的平均时间复杂度都是 。
- 总时间复杂度为 。
- 其中 是数组
- 空间复杂度:
- 哈希表
count在最坏情况下需要存储 个键值对。
- 哈希表
思考
-
贪心策略:
- 我们想要最小化连续卡牌数目,因此对于每个数字,我们只需要记录它最后一次出现的位置。
- 当再次遇到该数字时,与最后一次出现的位置配对,可以得到以当前位置结尾的最短连续子数组。
- 这种策略是贪心的,因为它总是选择最近的相同数字来最小化距离。
-
哈希表的作用:
- 哈希表用于记录每个数字最后一次出现的索引。
- 这样,当再次遇到该数字时,可以快速计算距离。
-
逻辑流程:
- 遍历每个卡牌
cards[i]。 - 检查当前卡牌值
num是否在哈希表中:- 是:说明之前出现过。计算从上次出现位置
left到当前位置i的连续卡牌数目i - left + 1,并更新最小值ans。 - 否:说明第一次出现,无需计算。
- 是:说明之前出现过。计算从上次出现位置
- 更新哈希表中
num的最后出现位置为i。
- 遍历每个卡牌
-
距离计算:
- 连续卡牌数目 = 结束索引 - 开始索引 + 1。
- 例如,
[3,4,2,3],开始索引为 0,结束索引为 3,长度为 3 - 0 + 1 = 4。
-
边界情况:
- 数组长度为 1:无法配对,返回 -1。
- 所有数字都不同:无法配对,返回 -1。
- 所有数字都相同:最小长度为 2(相邻的两个相同数字)。
思路二:滑动窗口
思路二:代码实现
1 | class Solution: |
思路二:📊 性能分析
- 时间复杂度:
- 其中 是数组
cards的长度。 - 虽然代码中有嵌套循环(
while),但每个元素最多被right指针访问一次,被left指针访问一次。 - 因此,总的时间复杂度是线性的 。
- 其中 是数组
- 空间复杂度:
- 哈希表
count在最坏情况下需要存储 个键值对。
- 哈希表
思考
-
滑动窗口原理:
- 我们维护一个窗口
[left, right]。 right指针不断向右移动,将新元素加入窗口。- 一旦窗口内某个数字的数量超过 1(即
count[num] > 1),说明找到了一个包含匹配对的子数组。 - 此时,我们需要尝试缩小窗口(移动
left指针),看看能否在保持“包含匹配对”的前提下获得更短的长度。
- 我们维护一个窗口
-
收缩逻辑:
- 当
count[num] > 1时,说明当前num是重复的。 - 我们记录当前窗口长度
right - left + 1并更新ans。 - 然后,我们将
cards[left]的计数减 1,并将left右移。
- 当
-
与思路一的对比:
- 思路一(哈希表记录位置):直接通过数学计算
i - last_pos + 1得到距离,更加直观且常数时间更优。 - 思路二(滑动窗口):是一种通用的处理“最小子数组”问题的方法。虽然在这里也能达到 ,但逻辑相对复杂一些,因为它需要维护窗口的收缩。
- 思路一(哈希表记录位置):直接通过数学计算
-
边界情况:
- 数组长度为 1:
count最大为 1,while循环不执行,返回 -1。 - 所有数字都不同:同上,返回 -1。
- 所有数字都相同:
right移动到 1 时,count变为 2,进入while,计算长度 2,收缩直到count变回 1。正确。
- 数组长度为 1:
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 笺札!






