Medium
题目描述
在宇宙 Earth C-137 中,Rick 发现了一种特殊的磁力形式,当两个球被放在他新发明的篮子里时会产生磁力。Rick 有 n 个空篮子,第 i 个篮子在位置 position[i]。Morty 有 m 个球,需要将球分配到篮子中,使得任意两个球之间的最小磁力最大。
Rick 指出,位置 x 和 y 处两个不同球之间的磁力是 |x - y|。
给定整数数组 position 和整数 m,返回所需的磁力。
示例 1:
输入:position = [1,2,3,4,7], m = 3
输出:3
解释:将 3 个球分别放在位置 1、4 和 7 的篮子中,球对之间的磁力为 [3, 3, 6]。最小磁力为 3。我们无法达到比 3 更大的最小磁力。
示例 2:
输入:position = [5,4,3,2,1,1000000000], m = 2
输出:999999999
解释:我们可以使用位置 1 和 1000000000 的篮子。
约束条件:
- n == position.length
- 2 <= n <= 10^5
- 1 <= position[i] <= 10^9
- position 中的所有整数都是不同的
- 2 <= m <= position.length
解题思路
这是一个经典的二分搜索 + 贪心验证的问题。
核心思路:
问题转化:我们需要找到最大的最小距离。如果能以距离 x 放置 m 个球,那么也能以更小的距离放置;反之,如果不能以距离 x 放置,那么更大的距离也不行。这说明答案具有单调性,可以用二分搜索。
二分搜索范围:
- 最小可能距离:1(相邻两个位置的最小差值)
- 最大可能距离:(max_pos - min_pos)(最远两个位置的距离)
贪心验证策略:给定一个距离 mid,判断是否能放置 m 个球:
- 将数组排序
- 贪心地放置球:第一个球放在最左边,之后每个球都放在距离上一个球至少 mid 距离的最左位置
- 如果能放下 m 个球,说明距离 mid 可行
算法流程:
- 对位置数组排序
- 二分搜索最大的可行距离
- 对每个候选距离,用贪心算法验证是否可行
时间复杂度主要来自排序 O(n log n) 和二分搜索中的验证 O(n log(max_distance))。
代码实现
class Solution {
public:
int maxDistance(vector<int>& position, int m) {
sort(position.begin(), position.end());
int left = 1, right = position.back() - position[0];
int result = 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (canPlace(position, m, mid)) {
result = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
}
private:
bool canPlace(vector<int>& position, int m, int minDist) {
int count = 1;
int lastPos = position[0];
for (int i = 1; i < position.size(); i++) {
if (position[i] - lastPos >= minDist) {
count++;
lastPos = position[i];
if (count >= m) return true;
}
}
return false;
}
};
class Solution:
def maxDistance(self, position: List[int], m: int) -> int:
position.sort()
def can_place(min_dist):
count = 1
last_pos = position[0]
for i in range(1, len(position)):
if position[i] - last_pos >= min_dist:
count += 1
last_pos = position[i]
if count >= m:
return True
return False
left, right = 1, position[-1] - position[0]
result = 1
while left <= right:
mid = (left + right) // 2
if can_place(mid):
result = mid
left = mid + 1
else:
right = mid - 1
return result
public class Solution {
public int MaxDistance(int[] position, int m) {
Array.Sort(position);
int left = 1, right = position[position.Length - 1] - position[0];
int result = 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (CanPlace(position, m, mid)) {
result = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
}
private bool CanPlace(int[] position, int m, int minDist) {
int count = 1;
int lastPos = position[0];
for (int i = 1; i < position.Length; i++) {
if (position[i] - lastPos >= minDist) {
count++;
lastPos = position[i];
if (count >= m) return true;
}
}
return false;
}
}
var maxDistance = function(position, m) {
position.sort((a, b) => a - b);
const canPlace = (minDist) => {
let count = 1;
let lastPos = position[0];
for (let i = 1; i < position.length; i++) {
if (position[i] - lastPos >= minDist) {
count++;
lastPos = position[i];
if (count >= m) return true;
}
}
return false;
};
let left = 1, right = position[position.length - 1] - position[0];
let result = 1;
while (left <= right) {
let mid = Math.floor((left + right) / 2);
if (canPlace(mid)) {
result = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
};
复杂度分析
| 复杂度类型 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 时间复杂度 | O(n log n + n log(max_distance)) | O(1) |
| 空间复杂度 | O(1) | O(1) |
说明:
- 时间复杂度:排序需要 O(n log n),二分搜索需要 O(log(max_distance)) 次,每次验证需要 O(n)
- 空间复杂度:只使用了常数额外空间(不考虑排序的空间开销)