Medium
题目描述
你有 n 个盒子。给你一个长度为 n 的二进制字符串 boxes,其中 boxes[i] 的值为 '0' 表示第 i 个盒子是空的,而 boxes[i] 的值为 '1' 表示盒子里有一个小球。
在一次操作中,你可以将一个小球从某个盒子移动到一个与之相邻的盒子中。第 i 个盒子和第 j 个盒子相邻需满足 abs(i - j) == 1。注意,操作执行后,某些盒子中可能会存在不止一个小球。
返回一个长度为 n 的数组 answer,其中 answer[i] 是将所有小球移动到第 i 个盒子所需的 最小 操作数。
每个 answer[i] 都需要根据盒子的 初始状态 进行计算。
示例 1:
输入:boxes = "110"
输出:[1,1,3]
解释:每个盒子对应的最小操作数如下:
1) 第 1 个盒子:将一个小球从第 2 个盒子移动到第 1 个盒子,需要 1 步操作。
2) 第 2 个盒子:将一个小球从第 1 个盒子移动到第 2 个盒子,需要 1 步操作。
3) 第 3 个盒子:将一个小球从第 1 个盒子移动到第 3 个盒子,需要 2 步操作。将一个小球从第 2 个盒子移动到第 3 个盒子,需要 1 步操作。共计 3 步操作。
示例 2:
输入:boxes = "001011"
输出:[11,8,5,4,3,4]
提示:
n == boxes.length1 <= n <= 2000boxes[i]为'0'或'1'
解题思路
这道题要求计算将所有球移动到每个盒子所需的最小操作数。我们可以使用几种解法:
方法一:直接计算法(O(n²)) 对于每个位置 i,遍历所有位置 j,如果位置 j 有球,则将球从 j 移动到 i 需要 |i-j| 步操作。这种方法时间复杂度为 O(n²),空间复杂度为 O(1)。
方法二:前缀和优化法(O(n))推荐 我们可以通过两次遍历来优化:
- 从左到右遍历,计算每个位置左边的球移动到当前位置的操作数
- 从右到左遍历,计算每个位置右边的球移动到当前位置的操作数
具体实现:
- 维护左边球的数量和累计操作数
- 维护右边球的数量和累计操作数
- 对于位置 i,左边的球每向右移动一位,操作数增加左边球的数量
- 对于位置 i,右边的球每向左移动一位,操作数增加右边球的数量
这种方法能够在线性时间内解决问题,是最优解法。
代码实现
class Solution {
public:
vector<int> minOperations(string boxes) {
int n = boxes.size();
vector<int> answer(n, 0);
// 从左到右计算
int leftBalls = 0, leftOps = 0;
for (int i = 0; i < n; i++) {
answer[i] += leftOps;
if (boxes[i] == '1') {
leftBalls++;
}
leftOps += leftBalls;
}
// 从右到左计算
int rightBalls = 0, rightOps = 0;
for (int i = n - 1; i >= 0; i--) {
answer[i] += rightOps;
if (boxes[i] == '1') {
rightBalls++;
}
rightOps += rightBalls;
}
return answer;
}
};
class Solution:
def minOperations(self, boxes: str) -> List[int]:
n = len(boxes)
answer = [0] * n
# 从左到右计算
left_balls = 0
left_ops = 0
for i in range(n):
answer[i] += left_ops
if boxes[i] == '1':
left_balls += 1
left_ops += left_balls
# 从右到左计算
right_balls = 0
right_ops = 0
for i in range(n - 1, -1, -1):
answer[i] += right_ops
if boxes[i] == '1':
right_balls += 1
right_ops += right_balls
return answer
public class Solution {
public int[] MinOperations(string boxes) {
int n = boxes.Length;
int[] answer = new int[n];
// 从左到右计算
int leftBalls = 0, leftOps = 0;
for (int i = 0; i < n; i++) {
answer[i] += leftOps;
if (boxes[i] == '1') {
leftBalls++;
}
leftOps += leftBalls;
}
// 从右到左计算
int rightBalls = 0, rightOps = 0;
for (int i = n - 1; i >= 0; i--) {
answer[i] += rightOps;
if (boxes[i] == '1') {
rightBalls++;
}
rightOps += rightBalls;
}
return answer;
}
}
/**
* @param {string} boxes
* @return {number[]}
*/
var minOperations = function(boxes) {
const n = boxes.length;
const answer = new Array(n).fill(0);
// 从左到右计算
let leftBalls = 0, leftOps = 0;
for (let i = 0; i < n; i++) {
answer[i] += leftOps;
if (boxes[i]
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 直接计算法 | O(n²) | O(1) |
| 前缀和优化法(推荐) | O(n) | O(1) |