力扣LeetCode: 1552 两球之间的磁力
题目:
在代号为 C-137 的地球上,Rick 发现如果他将两个球放在他新发明的篮子里,它们之间会形成特殊形式的磁力。Rick 有 n
个空的篮子,第 i
个篮子的位置在 position[i]
,Morty 想把 m
个球放到这些篮子里,使得任意两球间 最小磁力 最大。
已知两个球如果分别位于 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
解法:二分查找
解决思路
-
排序:
-
首先对
position
数组进行排序。排序后,我们可以更方便地计算任意两个篮子之间的距离。 -
例如,
position = [1, 2, 3, 4, 7]
,排序后仍然是[1, 2, 3, 4, 7]
。
-
-
二分查找:
-
我们需要找到最大的最小磁力。最小磁力的范围是从
0
到maxDistance
,其中maxDistance
是排序后数组中最后一个位置与第一个位置的差。 -
例如,
position = [1, 2, 3, 4, 7]
,maxDistance = 7 - 1 = 6
。 -
使用二分查找在
[0, maxDistance]
范围内搜索最大的最小磁力。
-
-
贪心选择:
-
对于二分查找的每一个中间值
mid
,我们需要检查是否可以放置m
个球,使得任意两个球之间的磁力至少为mid
。 -
如果可以,说明
mid
是一个可能的最小磁力,我们尝试更大的值。 -
如果不可以,说明
mid
太大,我们需要尝试更小的值。
-
-
检查函数:
-
实现一个辅助函数
canPlace
,用于检查是否可以放置m
个球,使得任意两个球之间的磁力至少为mid
。 -
通过遍历排序后的数组,贪心地放置球,确保每次放置的球与上一个放置的球的距离至少为
mid
。
-
代码实现
class Solution {
public:
int maxDistance(vector<int>& position, int m) {
// 1. 排序
sort(position.begin(), position.end());
// 2. 二分查找的范围
int left = 0, right = position.back() - position[0];
// 3. 二分查找
while (left < right) {
int mid = (left + right + 1) / 2; // 中间值
if (canPlace(position, m, mid)) {
left = mid; // 可以满足条件,尝试更大的值
} else {
right = mid - 1; // 不能满足条件,尝试更小的值
}
}
// 4. 返回结果
return left;
}
private:
// 检查是否可以放置 m 个球,使得任意两个球之间的磁力至少为 minDistance
bool canPlace(const vector<int>& position, int m, int minDistance) {
int count = 1; // 已放置的球的数量
int last = position[0]; // 上一个放置的球的位置
for (int i = 1; i < position.size(); ++i) {
if (position[i] - last >= minDistance) {
last = position[i]; // 放置当前球
count++; // 已放置的球的数量加 1
if (count >= m) {
return true; // 已经放置了 m 个球
}
}
}
return false; // 无法放置 m 个球
}
};
举例说明
示例 1:
-
输入:
position = [1, 2, 3, 4, 7]
,m = 3
-
排序后:
position = [1, 2, 3, 4, 7]
-
步骤:
-
二分查找范围:
left = 0
,right = 7 - 1 = 6
-
第一次循环:
-
mid = (0 + 6 + 1) / 2 = 3
-
检查是否可以放置 3 个球,磁力至少为 3:
-
放置
1
,然后放置4
(因为4 - 1 = 3 >= 3
),然后放置7
(因为7 - 4 = 3 >= 3
)。 -
可以满足条件,更新
left = 3
。
-
-
-
第二次循环:
-
mid = (3 + 6 + 1) / 2 = 5
-
检查是否可以放置 3 个球,磁力至少为 5:
-
放置
1
,然后无法放置其他球(因为2 - 1 = 1 < 5
,3 - 1 = 2 < 5
,4 - 1 = 3 < 5
,7 - 1 = 6 >= 5
)。 -
无法满足条件,更新
right = 4
。
-
-
-
第三次循环:
-
mid = (3 + 4 + 1) / 2 = 4
-
检查是否可以放置 3 个球,磁力至少为 4:
-
放置
1
,然后放置7
(因为7 - 1 = 6 >= 4
),但无法放置第三个球。 -
无法满足条件,更新
right = 3
。
-
-
-
循环结束,返回
left = 3
。
-
-
输出:
3
示例 2:
-
输入:
position = [5, 4, 3, 2, 1, 1000000000]
,m = 2
-
排序后:
position = [1, 2, 3, 4, 5, 1000000000]
-
步骤:
-
二分查找范围:
left = 0
,right = 1000000000 - 1 = 999999999
-
通过二分查找和贪心选择,最终找到最大最小磁力为
999999999
。
-
-
输出:
999999999
复杂度分析
-
时间复杂度:
-
排序:
O(n log n)
,其中n
是数组长度。 -
二分查找:
O(log(maxDistance))
,其中maxDistance
是最大位置差。 -
检查函数:
O(n)
,每次二分查找都需要调用一次检查函数。 -
总时间复杂度:
O(n log n + n log(maxDistance))
。
-
-
空间复杂度:
-
排序需要
O(log n)
的栈空间(快速排序的递归栈)。 -
其他操作使用常数空间。
-
总空间复杂度:
O(log n)
。
-