leetcode
leetcode 1001 ~ 1050
连接木棍的最低费用

连接木棍的最低费用

难度:

标签:

题目描述

代码结果

运行时间: 108 ms, 内存: 17.0 MB


/*
题目思路:
连接木棍的最低费用可以通过哈夫曼树的思想解决。
我们需要不断地将当前最短的两根木棍连接起来,并将其新的长度放回到候选池中,
重复这个过程直到所有的木棍都连接成一根。
在实现时,我们可以使用优先队列(最小堆)来每次快速找到最短的两根木棍。
使用Java Stream API无法直接解决该问题,因为它涉及到优先队列等数据结构。
但可以通过转换来辅助实现。
*/
import java.util.Arrays;
import java.util.PriorityQueue;

public class Solution {
    public int connectSticks(int[] sticks) {
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        Arrays.stream(sticks).forEach(pq::offer);
        int totalCost = 0;
        while (pq.size() > 1) {
            int first = pq.poll();
            int second = pq.poll();
            int cost = first + second;
            totalCost += cost;
            pq.offer(cost);
        }
        return totalCost;
    }
}

解释

方法:

这个问题可以通过优先队列(最小堆)来解决。首先,将所有的木棍长度放入一个最小堆中。在每一步中,从堆中取出两个最短的木棍,并计算将它们连接起来的成本,即两者之和。将这个成本加入到总成本中,并把新形成的木棍(即两者之和)加回到最小堆中。这个过程一直重复,直到堆中只剩下一个木棍。这时,累计的总成本就是连接所有木棍的最低费用。

时间复杂度:

O(n log n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在这个问题中选择使用最小堆(优先队列)而不是其他数据结构如数组或链表?
在这个问题中,我们需要频繁地找到并移除最小的元素,然后插入新的元素。使用最小堆(优先队列)可以有效地支持这些操作。最小堆能够在常数时间O(1)内提供最小元素,并且可以在对数时间O(log n)内完成删除最小元素和插入新元素的操作。相比之下,如果使用数组或链表,我们需要O(n)的时间来找到最小元素,插入操作也可能需要O(n)的时间(尤其是在数组中需要保持顺序时),这使得整体效率较低。因此,为了优化性能,选择最小堆是更合理的选择。
🦆
在取出两个最短的木棍进行合并时,如果它们的长度相同,会有什么特殊的影响吗?
如果在合并时取出的两根木棍长度相同,这并不会对算法的总体逻辑或成本计算产生特殊影响。无论木棍的长度是否相同,合并的成本都是两根木棍的长度之和。因此,这种情况只是一个特殊实例,但并不改变合并操作的基本成本计算或结果。
🦆
在合并木棍的过程中,每次都将新形成的木棍加回到最小堆中,这种操作是否会影响堆的性能,比如导致堆的重构?
将新形成的木棍加回到最小堆中确实需要重新调整堆来维持堆的性质,这是通过堆的上浮或下沉操作完成的,通常需要O(log n)时间。虽然这种操作涉及到堆的重构,但这是最小堆设计用来处理此类场景的,且是高效的。最小堆的整体结构使其非常适合频繁地进行插入和删除最小元素的操作,因此,虽然每次合并操作都需要堆的重构,但这并不会超出最小堆处理此类问题的性能预期。
🦆
合并操作一直持续到只剩一个木棍,这是否意味着总是存在一个最佳方案使得总成本最小,还是说结果依赖于木棍的初始排列顺序?
这个算法的目标是每次找到并合并最短的两根木棍,这种贪心策略确保了每一步的局部最优解,并最终达到全局最优的总成本。独立于木棍的初始排列顺序,这种方法通过确保每次操作都是成本最低的合并,从而达到整体成本最低。因此,总是存在一个最佳方案使得总成本最小,而这个结果不依赖于木棍的初始排列顺序。

相关问题

合并石头的最低成本

n 堆石头排成一排,第 i 堆中有 stones[i] 块石头。

每次 移动 需要将 连续的 k 堆石头合并为一堆,而这次移动的成本为这 k 堆中石头的总数。

返回把所有石头合并成一堆的最低成本。如果无法合并成一堆,返回 -1

 

示例 1:

输入:stones = [3,2,4,1], K = 2
输出:20
解释:
从 [3, 2, 4, 1] 开始。
合并 [3, 2],成本为 5,剩下 [5, 4, 1]。
合并 [4, 1],成本为 5,剩下 [5, 5]。
合并 [5, 5],成本为 10,剩下 [10]。
总成本 20,这是可能的最小值。

示例 2:

输入:stones = [3,2,4,1], K = 3
输出:-1
解释:任何合并操作后,都会剩下 2 堆,我们无法再进行合并。所以这项任务是不可能完成的。.

示例 3:

输入:stones = [3,5,1,2,6], K = 3
输出:25
解释:
从 [3, 5, 1, 2, 6] 开始。
合并 [5, 1, 2],成本为 8,剩下 [3, 8, 6]。
合并 [3, 8, 6],成本为 17,剩下 [17]。
总成本 25,这是可能的最小值。

 

提示:

  • n == stones.length
  • 1 <= n <= 30
  • 1 <= stones[i] <= 100
  • 2 <= k <= 30