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.length
  • 1 <= n <= 2000
  • boxes[i]'0''1'

解题思路

这道题要求计算将所有球移动到每个盒子所需的最小操作数。我们可以使用几种解法:

方法一:直接计算法(O(n²)) 对于每个位置 i,遍历所有位置 j,如果位置 j 有球,则将球从 j 移动到 i 需要 |i-j| 步操作。这种方法时间复杂度为 O(n²),空间复杂度为 O(1)。

方法二:前缀和优化法(O(n))推荐 我们可以通过两次遍历来优化:

  1. 从左到右遍历,计算每个位置左边的球移动到当前位置的操作数
  2. 从右到左遍历,计算每个位置右边的球移动到当前位置的操作数

具体实现:

  • 维护左边球的数量和累计操作数
  • 维护右边球的数量和累计操作数
  • 对于位置 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)

相关题目