leetcode
leetcode 1501 ~ 1550
数组的最小偏移量

数组的最小偏移量

难度:

标签:

题目描述

代码结果

运行时间: 181 ms, 内存: 23.2 MB


/**
 * Problem: Minimize the deviation in an array using Java Streams
 * Approach:
 * 1. Transform odd numbers to even by multiplying by 2 and find the minimum element.
 * 2. Use a TreeSet to keep sorted elements and find the current deviation.
 * 3. Continuously update the max element in the set by dividing it by 2 until all elements are odd.
 */
import java.util.*;
import java.util.stream.Collectors;

public class Solution {
    public int minimumDeviation(int[] nums) {
        TreeSet<Integer> set = new TreeSet<>();

        // Preprocess the array to handle all numbers and create a sorted set
        Arrays.stream(nums)
              .map(num -> (num % 2 == 1) ? num * 2 : num) // Make odd numbers even
              .forEach(set::add);

        int minDeviation = Integer.MAX_VALUE;

        while (true) {
            int max = set.last();
            int min = set.first();
            minDeviation = Math.min(minDeviation, max - min);

            if (max % 2 == 1) break; // Stop when max is odd

            set.remove(max);
            set.add(max / 2);
        }

        return minDeviation;
    }
}

解释

方法:

首先,为了确保每个数字都可以有效地进行操作(即奇数乘以2变偶数,偶数除以2直到成为奇数),我们将所有奇数都乘以2以统一初始状态。使用最大堆(实际上使用了负数实现最小堆的逆序)来存储这些转换后的数字,以便快速获取当前的最大值。堆的最大值(即负数绝对值最小)代表了当前数组的最大值。我们持续从堆中取出最大值,并尝试将其减半(即最大偶数除以2),然后将新值推回堆中,更新当前的最大值。通过这种方式,我们试图不断缩小数组元素之间的最大差异,即偏移量。每次操作后,都会更新当前的最小偏移量,直到堆顶元素为奇数为止(因为奇数不能再减半)。

时间复杂度:

O(n log(n) log(maxNum))

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在开始时需要将所有奇数乘以2来统一初始状态?这样操作的意义和结果是什么?
在开始时将所有奇数乘以2是为了简化操作和统一处理流程。由于奇数乘以2后变成偶数,这样一来,我们可以统一地对数组中的数进行除以2的操作,直至它们不再是偶数(即变为奇数)。这种做法的目的是使得所有的数字都能通过相同的操作(即连续除以2)达到其最小可能值,从而更方便地比较和计算最小偏移量。此外,这样也确保了在最大堆中每个数字都可以经过相同的处理流程,简化了问题的复杂度。
🦆
在使用最大堆的策略中,为什么选择用负数来实现最小堆的逆序而不是直接使用最大堆?
在Python中,标准库heapq实现的是最小堆,而不是最大堆。因此,为了通过heapq库来获取最大堆的效果,我们可以将所有元素的符号取反(即乘以-1),这样原本数值大的现在变为最小,便可以利用最小堆的性质来模拟最大堆的操作。这种方法免去了自己实现一个最大堆的复杂性,利用现有的库简化了代码实现。
🦆
堆的最大值通过负数的绝对值最小来表示,这种表示方式在算法中如何确保正确性和效率?
使用负数来表示堆中的元素,实际上是借助了最小堆的性质来模拟最大堆。在这种情况下,最大值(原本的正数中的最大值)通过取负变成了最小值,因此在最小堆中位于顶部。这种表示方法的正确性保证在于数值转化的一致性(即所有数都取相反数),而效率则来源于Python的heapq库,该库为二叉堆提供了高效的实现,对于堆操作(如插入、删除顶部元素)均能在对数时间内完成。
🦆
你提到每次从堆中取出最大值并尝试将其减半,这个操作如何影响堆的结构和后续操作?
每次从堆中取出最大值(即堆顶的负数绝对值最小的数),并将其减半后,再次加入堆中,这个操作实际上是更新了堆的结构。由于我们是将最大值减半,所以重新加入堆中的数要么还是当前的最大值,要么变得更小。这会导致堆的重新调整(heapify),保证了堆顶始终是当前最大值的负数。这种动态更新的过程是必要的,因为它保证了我们每次都能获取到当前数组中的最大值,并据此来尝试减少最大和最小值之间的差异,直到达到可能的最小偏移量。

相关问题