1167. Minimum Cost to Connect Sticks

Problem Description

You are given an array sticks where each element represents the length of a stick (positive integer).

You can connect any two sticks together to form a single stick. When you connect two sticks with lengths x and y, the resulting stick has length x + y, and you must pay a cost equal to x + y.

Your goal is to connect all the sticks together until only one stick remains. You need to find the minimum total cost to achieve this.

For example, if you have sticks with lengths [2, 4, 3]:

  • You could first connect sticks of length 2 and 3, paying cost 5, resulting in sticks [5, 4]
  • Then connect the remaining two sticks of length 5 and 4, paying cost 9
  • Total cost would be 5 + 9 = 14

The strategy uses a greedy approach with a min heap. By always connecting the two shortest sticks first, we minimize the cost at each step. The algorithm repeatedly:

  1. Extracts the two smallest sticks from the heap
  2. Connects them (calculating the cost)
  3. Adds the cost to the running total
  4. Inserts the new combined stick back into the heap
  5. Continues until only one stick remains
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that when we connect two sticks, the resulting stick will potentially be used in future connections. Since we pay the sum of lengths each time we connect, longer sticks that participate in multiple connections will contribute their length to the total cost multiple times.

Think of it this way: if we have a very long stick, we want to use it as few times as possible in our connections. Conversely, shorter sticks should be combined early because their smaller values will be added to the total cost multiple times as they form parts of larger sticks.

Consider why connecting the two shortest sticks is optimal at each step:

  • When we connect sticks of length a and b, we pay a + b
  • The resulting stick of length a + b might be used again in future connections
  • If we had instead connected a short stick with a long stick early, that long stick's value would be included in more subsequent operations, increasing the total cost

This naturally leads to a greedy strategy: always connect the two shortest available sticks. By doing this, we ensure that:

  1. Smaller values are "promoted" to larger sticks early
  2. Larger values participate in fewer connection operations
  3. The overall cost is minimized

A min heap (priority queue) is the perfect data structure for this approach because it allows us to efficiently:

  • Extract the two minimum elements in O(log n) time
  • Insert the newly formed stick back in O(log n) time
  • Always maintain access to the current shortest sticks

Learn more about Greedy and Heap (Priority Queue) patterns.

Solution Implementation

1from typing import List 2from heapq import heapify, heappop, heappush 3 4class Solution: 5 def connectSticks(self, sticks: List[int]) -> int: 6 # Convert the list into a min-heap in-place 7 heapify(sticks) 8 9 # Track the total cost of connecting all sticks 10 total_cost = 0 11 12 # Keep combining sticks until only one remains 13 while len(sticks) > 1: 14 # Extract the two smallest sticks from the heap 15 first_stick = heappop(sticks) 16 second_stick = heappop(sticks) 17 18 # Calculate the cost of combining these two sticks 19 combined_length = first_stick + second_stick 20 21 # Add this combination cost to the total 22 total_cost += combined_length 23 24 # Push the combined stick back into the heap 25 heappush(sticks, combined_length) 26 27 return total_cost 28
1class Solution { 2 /** 3 * Connects sticks with minimum cost using a greedy approach. 4 * The strategy is to always combine the two smallest sticks first. 5 * 6 * @param sticks Array of stick lengths to be connected 7 * @return The minimum total cost to connect all sticks into one 8 */ 9 public int connectSticks(int[] sticks) { 10 // Min heap to store stick lengths, ensuring smallest elements are accessed first 11 PriorityQueue<Integer> minHeap = new PriorityQueue<>(); 12 13 // Add all stick lengths to the min heap 14 for (int stickLength : sticks) { 15 minHeap.offer(stickLength); 16 } 17 18 // Track the total cost of connecting all sticks 19 int totalCost = 0; 20 21 // Keep combining sticks until only one remains 22 while (minHeap.size() > 1) { 23 // Extract the two smallest sticks 24 int firstSmallest = minHeap.poll(); 25 int secondSmallest = minHeap.poll(); 26 27 // Calculate the cost of combining these two sticks 28 int combinedLength = firstSmallest + secondSmallest; 29 30 // Add the combination cost to the total 31 totalCost += combinedLength; 32 33 // Put the combined stick back into the heap for further combinations 34 minHeap.offer(combinedLength); 35 } 36 37 return totalCost; 38 } 39} 40
1class Solution { 2public: 3 int connectSticks(vector<int>& sticks) { 4 // Use a min-heap to always access the smallest sticks efficiently 5 // This ensures we minimize the cost by combining smallest sticks first 6 priority_queue<int, vector<int>, greater<int>> minHeap; 7 8 // Add all sticks to the min-heap 9 for (const int& stickLength : sticks) { 10 minHeap.push(stickLength); 11 } 12 13 // Track the total cost of connecting all sticks 14 int totalCost = 0; 15 16 // Keep combining sticks until only one remains 17 while (minHeap.size() > 1) { 18 // Extract the two smallest sticks 19 int firstSmallest = minHeap.top(); 20 minHeap.pop(); 21 22 int secondSmallest = minHeap.top(); 23 minHeap.pop(); 24 25 // Calculate the cost of combining these two sticks 26 int combinedLength = firstSmallest + secondSmallest; 27 28 // Add the cost to the total 29 totalCost += combinedLength; 30 31 // Push the combined stick back to the heap for further combinations 32 minHeap.push(combinedLength); 33 } 34 35 return totalCost; 36 } 37}; 38
1// Comparison function type for generic heap operations 2type Compare<T> = (lhs: T, rhs: T) => number; 3 4// Heap data structure array with null at index 0 for easier indexing 5let heapData: Array<number | null> = [null]; 6 7// Comparison function for heap operations 8let heapCompare: Compare<number> = (lhs: number, rhs: number) => 9 (lhs < rhs ? -1 : lhs > rhs ? 1 : 0); 10 11// Less than comparison using heap indices 12function isLessThan(i: number, j: number): boolean { 13 return heapCompare(heapData[i]!, heapData[j]!) < 0; 14} 15 16// Swap two elements in the heap by their indices 17function swapElements(i: number, j: number): void { 18 [heapData[i], heapData[j]] = [heapData[j], heapData[i]]; 19} 20 21// Initialize heap with given array of numbers 22function initializeHeap(sticks: number[]): void { 23 heapData = [null, ...sticks]; 24 // Build heap from bottom up 25 for (let i = heapSize(); i > 0; i--) { 26 heapify(i); 27 } 28} 29 30// Get current size of the heap (excluding null at index 0) 31function heapSize(): number { 32 return heapData.length - 1; 33} 34 35// Add a new element to the heap and maintain heap property 36function heapPush(value: number): void { 37 heapData.push(value); 38 let currentIndex = heapSize(); 39 // Bubble up: swap with parent while current is smaller 40 while (currentIndex >> 1 !== 0 && isLessThan(currentIndex, currentIndex >> 1)) { 41 swapElements(currentIndex, currentIndex >> 1); 42 currentIndex = currentIndex >> 1; 43 } 44} 45 46// Remove and return the minimum element from the heap 47function heapPop(): number { 48 // Move last element to root 49 swapElements(1, heapSize()); 50 const minElement = heapData.pop(); 51 // Restore heap property from root 52 heapify(1); 53 return minElement!; 54} 55 56// Restore heap property starting from index i (bubble down) 57function heapify(i: number): void { 58 while (true) { 59 let minIndex = i; 60 const leftChild = i * 2; 61 const rightChild = i * 2 + 1; 62 const heapLength = heapData.length; 63 64 // Find minimum among node and its children 65 if (leftChild < heapLength && isLessThan(leftChild, minIndex)) { 66 minIndex = leftChild; 67 } 68 if (rightChild < heapLength && isLessThan(rightChild, minIndex)) { 69 minIndex = rightChild; 70 } 71 72 // If minimum is not the current node, swap and continue 73 if (minIndex !== i) { 74 swapElements(i, minIndex); 75 i = minIndex; 76 } else { 77 break; 78 } 79 } 80} 81 82// Main function to connect sticks with minimum cost 83// Strategy: Always connect the two smallest sticks to minimize total cost 84function connectSticks(sticks: number[]): number { 85 // Initialize min heap with all sticks 86 initializeHeap(sticks); 87 88 let totalCost = 0; 89 90 // Keep connecting sticks until only one remains 91 while (heapSize() > 1) { 92 // Get two smallest sticks 93 const firstStick = heapPop(); 94 const secondStick = heapPop(); 95 96 // Cost of connecting is sum of both sticks 97 const connectionCost = firstStick + secondStick; 98 totalCost += connectionCost; 99 100 // Add the new combined stick back to heap 101 heapPush(connectionCost); 102 } 103 104 return totalCost; 105} 106

Solution Approach

The solution implements the greedy strategy using Python's built-in heapq module, which provides a min heap implementation.

Step 1: Initialize the Min Heap

heapify(sticks)

We transform the input array sticks into a min heap in-place. This operation takes O(n) time and ensures the smallest element is always at index 0.

Step 2: Initialize Cost Counter

ans = 0

We maintain a variable ans to track the cumulative cost of all connections.

Step 3: Connect Sticks Until One Remains

while len(sticks) > 1:

We continue the process as long as there are at least 2 sticks to connect.

Step 4: Extract and Connect Two Smallest Sticks

z = heappop(sticks) + heappop(sticks)

In each iteration:

  • heappop(sticks) removes and returns the smallest stick (first call)
  • heappop(sticks) removes and returns the second smallest stick (second call)
  • We add their lengths to get the new stick length z

Step 5: Accumulate Cost

ans += z

The cost of this connection (which equals the length of the new stick) is added to our total cost.

Step 6: Insert New Stick Back

heappush(sticks, z)

The newly formed stick is inserted back into the heap, maintaining the heap property.

Time Complexity: O(n log n) where n is the number of sticks. We perform approximately n-1 iterations, and each iteration involves two heappop operations and one heappush operation, each taking O(log n) time.

Space Complexity: O(1) extra space since we modify the input array in-place (not counting the space used by the input itself).

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with sticks = [1, 8, 3, 5]:

Initial State:

  • Array: [1, 8, 3, 5]
  • After heapify: [1, 3, 8, 5] (min heap structure)
  • Total cost: 0

Iteration 1:

  • Extract two smallest: 1 and 3
  • Connect them: 1 + 3 = 4
  • Add cost: total = 0 + 4 = 4
  • Insert back: heap becomes [4, 5, 8]

Iteration 2:

  • Extract two smallest: 4 and 5
  • Connect them: 4 + 5 = 9
  • Add cost: total = 4 + 9 = 13
  • Insert back: heap becomes [8, 9]

Iteration 3:

  • Extract two smallest: 8 and 9
  • Connect them: 8 + 9 = 17
  • Add cost: total = 13 + 17 = 30
  • Insert back: heap becomes [17]

Final Result:

  • Only one stick remains
  • Minimum total cost = 30

The greedy approach ensures we always combine the smallest sticks first, minimizing how many times larger values contribute to the total cost. Notice how the stick of length 8 (initially the largest) only participated in one connection operation, while smaller sticks like 1 and 3 had their values included multiple times through the combined sticks they formed.

Time and Space Complexity

The time complexity is O(n × log n), where n is the length of the array sticks.

  • The initial heapify(sticks) operation takes O(n) time to build a min-heap from the array.
  • The while loop runs n - 1 times (until only one stick remains).
  • In each iteration, we perform:
    • Two heappop() operations: O(log n) each
    • One heappush() operation: O(log n)
    • Total per iteration: O(log n)
  • Since we have n - 1 iterations, the loop contributes O(n × log n) time.
  • Overall time complexity: O(n) + O(n × log n) = O(n × log n)

The space complexity is O(n), where n is the length of the array sticks.

  • The heapify() operation modifies the input array in-place and uses O(1) extra space.
  • The heap operations (heappop and heappush) work on the existing array without requiring additional space proportional to the input size.
  • The space is dominated by the input array itself, which has size n.
  • Therefore, the space complexity is O(n).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Edge Case: Single or Empty Stick Array

Pitfall: The code doesn't explicitly handle the case where the input contains only one stick or is empty. While the current implementation works correctly (returning 0 for a single stick), it's not immediately obvious why.

Why it happens: When len(sticks) == 1, the while loop never executes, and total_cost remains 0. This is correct since no connections are needed, but it's implicit rather than explicit.

Solution:

def connectSticks(self, sticks: List[int]) -> int:  # Explicitly handle edge cases for clarity  if len(sticks) <= 1:  return 0   heapify(sticks)  total_cost = 0  # ... rest of the code

2. Using Wrong Heap Type (Max Heap Instead of Min Heap)

Pitfall: Attempting to use a max heap or forgetting that Python's heapq is a min heap by default. Some might try to negate values to simulate a max heap, which would give incorrect results for this problem.

Why it happens: In some problems, we need a max heap and use negation as a workaround. Applying this pattern here would cause us to connect the largest sticks first, resulting in suboptimal cost.

Incorrect approach example:

# WRONG: Don't negate values for this problem! heapify([-x for x in sticks]) # This would create wrong behavior

Solution: Use the min heap directly as shown in the original code. The algorithm requires connecting smallest sticks first.

3. Modifying Original Input Without Consideration

Pitfall: The solution modifies the input array sticks in-place. If the caller expects the original array to remain unchanged, this causes unexpected side effects.

Why it happens: Using heapify(sticks) modifies the original list structure. After the function returns, the original sticks array is altered.

Solution: Create a copy if preserving the original is important:

def connectSticks(self, sticks: List[int]) -> int:  # Create a copy to avoid modifying the original  sticks_heap = sticks.copy() # or sticks[:]  heapify(sticks_heap)   total_cost = 0  while len(sticks_heap) > 1:  combined_length = heappop(sticks_heap) + heappop(sticks_heap)  total_cost += combined_length  heappush(sticks_heap, combined_length)   return total_cost

4. Inefficient Two-Line Pop Operations

Pitfall: Some might write the extraction of two sticks in separate lines with intermediate variables when not needed for clarity.

Less efficient style:

first = heappop(sticks) second = heappop(sticks) combined = first + second

Why it matters: While functionally correct, it uses more lines and variables than necessary. The original compact form z = heappop(sticks) + heappop(sticks) is more concise.

Note: This is more about style than correctness. Choose based on team preferences for readability vs. conciseness.

Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Problem: Given a list of tasks and a list of requirements, compute a sequence of tasks that can be performed, such that we complete every task once while satisfying all the requirements.

Which of the following method should we use to solve this problem?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More