Medium
题目描述
给定一个整数数组 nums。
如果 nums[i] == nums[j] == nums[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 <= 10^51 <= nums[i] <= n
解题思路
这道题的关键在于理解距离公式和寻找最优三元组。
核心观察:
- 距离公式
abs(i - j) + abs(j - k) + abs(k - i)可以简化。对于三个有序的下标p < q < r,距离等于2 * (r - p) - 要形成好元组,相同的数字必须至少出现 3 次
- 为了最小化距离,我们需要找到相同数字的三个下标,使得最大下标与最小下标的差值最小
算法思路:
- 使用哈希表记录每个数字出现的所有下标位置
- 对于每个至少出现 3 次的数字,遍历其所有下标组合
- 由于下标是按顺序记录的,我们只需要考虑每三个连续的下标,这样能保证
r - p最小 - 计算所有可能组合的距离,返回最小值
时间复杂度优化: 由于我们只需要考虑连续的三个下标,对于有 k 个下标的数字,只需要检查 k-2 个三元组,而不是 C(k,3) 个。
代码实现
class Solution {
public:
int minimumDistance(vector<int>& nums) {
unordered_map<int, vector<int>> positions;
// 记录每个数字出现的位置
for (int i = 0; i < nums.size(); i++) {
positions[nums[i]].push_back(i);
}
int minDistance = INT_MAX;
bool found = false;
// 遍历每个数字
for (auto& [num, pos] : positions) {
if (pos.size() < 3) continue;
// 检查每三个连续的位置
for (int i = 0; i <= (int)pos.size() - 3; i++) {
int p = pos[i], q = pos[i + 1], r = pos[i + 2];
int distance = 2 * (r - p);
minDistance = min(minDistance, distance);
found = true;
}
}
return found ? minDistance : -1;
}
};
class Solution:
def minimumDistance(self, nums: List[int]) -> int:
from collections import defaultdict
positions = defaultdict(list)
# 记录每个数字出现的位置
for i, num in enumerate(nums):
positions[num].append(i)
min_distance = float('inf')
found = False
# 遍历每个数字
for num, pos in positions.items():
if len(pos) < 3:
continue
# 检查每三个连续的位置
for i in range(len(pos) - 2):
p, q, r = pos[i], pos[i + 1], pos[i + 2]
distance = 2 * (r - p)
min_distance = min(min_distance, distance)
found = True
return min_distance if found else -1
public class Solution {
public int MinimumDistance(int[] nums) {
var positions = new Dictionary<int, List<int>>();
// 记录每个数字出现的位置
for (int i = 0; i < nums.Length; i++) {
if (!positions.ContainsKey(nums[i])) {
positions[nums[i]] = new List<int>();
}
positions[nums[i]].Add(i);
}
int minDistance = int.MaxValue;
bool found = false;
// 遍历每个数字
foreach (var kvp in positions) {
var pos = kvp.Value;
if (pos.Count < 3) continue;
// 检查每三个连续的位置
for (int i = 0; i <= pos.Count - 3; i++) {
int p = pos[i], q = pos[i + 1], r = pos[i + 2];
int distance = 2 * (r - p);
minDistance = Math.Min(minDistance, distance);
found = true;
}
}
return found ? minDistance : -1;
}
}
var minimumDistance = function(nums) {
const positions = new Map();
// 记录每个数字出现的位置
for (let i = 0; i < nums.length; i++) {
if (!positions.has(nums[i])) {
positions.set(nums[i], []);
}
positions.get(nums[i]).push(i);
}
let minDistance = Infinity;
let found = false;
// 遍历每个数字
for (const [num, pos] of positions) {
if (pos.length < 3) continue;
// 检查每三个连续的位置
for (let i = 0; i <= pos.length - 3; i++) {
const p = pos[i], q = pos[i + 1], r = pos[i + 2];
const distance = 2 * (r - p);
minDistance = Math.min(minDistance, distance);
found = true;
}
}
return found ? minDistance : -1;
};
复杂度分析
| 复杂度类型 | 分析 |
|---|---|
| 时间复杂度 | O(n),其中 n 是数组长度。需要遍历数组一次记录位置,然后对每个数字的位置列表进行线性扫描,总体时间复杂度为线性 |
| 空间复杂度 | O(n),最坏情况下需要存储所有元素的位置信息 |