leetcode
leetcode 2251 ~ 2300
将整数减少到零需要的最少操作数

将整数减少到零需要的最少操作数

难度:

标签:

题目描述

You are given a positive integer n, you can do the following operation any number of times:

  • Add or subtract a power of 2 from n.

Return the minimum number of operations to make n equal to 0.

A number x is power of 2 if x == 2i where i >= 0.

 

Example 1:

Input: n = 39
Output: 3
Explanation: We can do the following operations:
- Add 20 = 1 to n, so now n = 40.
- Subtract 23 = 8 from n, so now n = 32.
- Subtract 25 = 32 from n, so now n = 0.
It can be shown that 3 is the minimum number of operations we need to make n equal to 0.

Example 2:

Input: n = 54
Output: 3
Explanation: We can do the following operations:
- Add 21 = 2 to n, so now n = 56.
- Add 23 = 8 to n, so now n = 64.
- Subtract 26 = 64 from n, so now n = 0.
So the minimum number of operations is 3.

 

Constraints:

  • 1 <= n <= 105

代码结果

运行时间: 18 ms, 内存: 16.1 MB


/*
 * 思路:
 * 1. 计算小于 n 的所有 2 的幂次方。
 * 2. 使用广度优先搜索(BFS)找到到达 0 的最短路径。
 * 3. 使用 Stream 处理数据。
 */
import java.util.*;
import java.util.stream.IntStream;

public class MinOperationsToZeroStream {
    public int minOperations(int n) {
        Set<Integer> visited = new HashSet<>();
        Queue<Integer> queue = new LinkedList<>();
        queue.add(n);
        visited.add(n);
        int operations = 0;

        // 计算所有可能的2的幂次方
        int[] powersOfTwo = IntStream.range(0, 18)
                .map(i -> 1 << i)
                .toArray();

        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                int current = queue.poll();
                if (current == 0) return operations;

                for (int power : powersOfTwo) {
                    int next1 = current + power;
                    int next2 = current - power;

                    if (visited.add(next1)) queue.offer(next1);
                    if (visited.add(next2)) queue.offer(next2);
                }
            }
            operations++;
        }

        return -1; // 不会到达这里
    }
}

解释

方法:

这个题解采用了一种贪心的思路。首先,将整数 n 转换为二进制表示。然后从最低位开始遍历,每次遇到 1 时,进行一次操作,将该位变为 0。如果这个 1 的左边还有 1,则需要额外进行一次操作,将左边的 1 都变为 0,最左边的 0 变为 1。这样做的原因是,每次操作都是加上或减去 2 的某个幂,所以在二进制表示中,就是在某个位置加上或减去 1。这个过程一直进行,直到整个二进制表示中没有 1 为止。

时间复杂度:

O(log n)

空间复杂度:

O(log n)

代码细节讲解

🦆
为何在处理二进制表示中的每个位时,遇到1就要进行加或减操作?这种操作如何保证最终能够使n等于0?
在二进制中,每个1表示2的幂次的和。为了将数字减到0,我们需要消除所有的1。每次遇到1时进行加或减操作,实质上是消除当前位上的1,即将其变为0。通过对每个1位执行操作,逐步将所有的1清零,最终会使得二进制表示中没有1,即数字变为0。这种方法通过逐位处理保证了最终n等于0。
🦆
在题解中提到,如果1的左边还有1,则需要额外进行一次操作,这种操作的逻辑是如何确定的?即为什么要将最左边的0变为1?
这种操作是基于贪心策略的考虑。当连续的1(如1101中的11)存在时,仅将当前位的1变为0(变为1001)还不足以最优化操作次数。继续进行操作,将左边的1也变为0(变为0001),然后将最左边的0(如果存在)变为1(变为1001),是为了在下一轮操作中减少1的数量,从而更快达到目标。这种操作通过在可能的情况下减少连续1的数量,提高了减少到零的效率。
🦆
题解中提到,当整个二进制表示中没有1为止时,过程才结束。这是否意味着对于每个位的处理都是必需的,即使这个位后面全是0?
是的,对于每个位的处理都是必需的。虽然后面的位可能全是0,但处理每个位是为了确保当前位上的1被正确地转换或消除。此外,即便后面都是0,当前位上的1如果不处理,仍然会保留其数值,使n不为0。因此,必须处理每个位上的1,直到所有的1都被消除。
🦆
题解中没有详细说明如何处理二进制数的最高位。当最高位为1,且左边没有更高的位时,我们如何处理这种情况?
当最高位为1且左边没有更高的位时,这个1表示数字中最大的2的幂。处理这种情况的方法是简单的:将此1转换为0。因为没有更高的位,所以不需要额外的操作来增加更高位的1或减少位数。这个操作将直接减少n的值,逐步接近0。在这种情况下,只需要一次操作即可处理最高位的1。

相关问题