LeetCode 2422 - Merge Operations to Turn Array Into a Palindrome
The problem asks us to transform a given array of positive integers into a palindrome using the minimum number of operations, where each operation consists of taking two adjacent elements and replacing them with their sum.
Difficulty: 🟡 Medium
Topics: Array, Two Pointers, Greedy
Solution
Problem Understanding
The problem asks us to transform a given array of positive integers into a palindrome using the minimum number of operations, where each operation consists of taking two adjacent elements and replacing them with their sum. A palindrome is an array that reads the same forward and backward, so for an array nums, we require nums[i] == nums[n-1-i] for all valid indices i.
The input is a list of integers nums with size up to 10^5 and values up to 10^6. The output is a single integer representing the minimum number of merge operations required. Edge cases include arrays that are already palindromes, arrays with a single element, or arrays where repeated merging is necessary to collapse the array into a single element (which is trivially a palindrome).
The constraints imply that any solution must operate in roughly O(n) or O(n log n) time; an approach that simulates all possible merges would be far too slow.
Key observations include that only adjacent elements can be merged, so the strategy involves balancing the sums from both ends of the array to gradually make the left and right halves equal.
Approaches
Brute Force
A brute-force approach would attempt all possible sequences of merges to see which sequence produces a palindrome in the fewest steps. This could be done recursively, merging adjacent elements and tracking the resulting array until a palindrome is reached. Although correct, this approach is exponential in time complexity because there are many ways to merge elements, making it infeasible for large n (n <= 10^5).
Optimal Approach
The optimal approach uses a two-pointer greedy method. The idea is to maintain two pointers, left and right, starting at the ends of the array. If the elements at left and right are equal, we move both pointers inward. If nums[left] < nums[right], we merge nums[left] with its right neighbor and increment a merge count. Conversely, if nums[left] > nums[right], we merge nums[right] with its left neighbor. We continue until the pointers meet or cross. This approach works because each merge reduces the array size while keeping the palindrome property achievable greedily from the ends.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(2^n) | O(n) | Tries all possible merge sequences, infeasible for large arrays |
| Optimal | O(n) | O(1) | Two-pointer greedy method, merges elements from the ends |
Algorithm Walkthrough
-
Initialize two pointers
leftat0andrightatlen(nums) - 1. Initializeoperationsto0. -
While
left < right: -
If
nums[left] == nums[right], the pair is already balanced, so moveleftforward andrightbackward. -
If
nums[left] < nums[right], mergenums[left]withnums[left + 1]by adding the two values, replacenums[left + 1]with the sum, and incrementoperations. Do not moveright. -
If
nums[left] > nums[right], mergenums[right]withnums[right - 1]by adding the two values, replacenums[right - 1]with the sum, and incrementoperations. Do not moveleft. -
Repeat until
left >= right. -
Return the total
operationscount.
Why it works: Each merge moves the array closer to a palindrome. By always merging the smaller side, we balance the sums incrementally. The two-pointer approach ensures that we do the minimum number of merges necessary because each operation directly addresses the inequality between the current left and right elements.
Python Solution
from typing import List
class Solution:
def minimumOperations(self, nums: List[int]) -> int:
left, right = 0, len(nums) - 1
operations = 0
while left < right:
if nums[left] == nums[right]:
left += 1
right -= 1
elif nums[left] < nums[right]:
nums[left + 1] += nums[left]
left += 1
operations += 1
else: # nums[left] > nums[right]
nums[right - 1] += nums[right]
right -= 1
operations += 1
return operations
This implementation uses two pointers to compare elements from both ends. If the left element is smaller, it merges it with the next element on the left side. If the right element is smaller, it merges it with the previous element on the right side. Each merge increments the operations counter.
Go Solution
func minimumOperations(nums []int) int {
left, right := 0, len(nums)-1
operations := 0
for left < right {
if nums[left] == nums[right] {
left++
right--
} else if nums[left] < nums[right] {
nums[left+1] += nums[left]
left++
operations++
} else { // nums[left] > nums[right]
nums[right-1] += nums[right]
right--
operations++
}
}
return operations
}
The Go implementation follows the same logic. Slices are updated in place, and integer overflow is not an issue given the problem constraints (nums[i] <= 10^6 and length <= 10^5).
Worked Examples
Example 1: [4,3,2,1,2,3,1]
| Step | Left | Right | Action | Array State | Operations |
|---|---|---|---|---|---|
| 1 | 0 | 6 | nums[0]=4, nums[6]=1 → merge right | [4,3,2,1,2,4] | 1 |
| 2 | 0 | 5 | nums[0]=4, nums[5]=4 → equal → move inward | [4,3,2,1,2,4] | 1 |
| 3 | 1 | 4 | nums[1]=3, nums[4]=2 → merge right | [4,5,2,1,2,4] | 2 |
| 4 | 2 | 3 | nums[2]=2, nums[3]=1 → merge right | [4,5,3,2,4] | 3 |
| Continue until palindrome reached | [4,3,2,3,4] | 2 |
Example 2: [1,2,3,4]
| Step | Left | Right | Action | Array State | Operations |
|---|---|---|---|---|---|
| 1 | 0 | 3 | 1<4 → merge left | [3,2,3,4] | 1 |
| 2 | 1 | 3 | 2<4 → merge left | [3,5,3,4] | 2 |
| 3 | 1 | 2 | 5>3 → merge right | [3,8,4] | 3 |
| 4 | 1 | 1 | done | [3,8,4] → final palindrome [10] | 3 |
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Each pointer moves at most n steps, each merge is O(1) |
| Space | O(1) | Array is modified in place, no extra data structures |
The two-pointer method ensures linear processing of the array without extra storage.
Test Cases
# Provided examples
assert Solution().minimumOperations([4,3,2,1,2,3,1]) == 2 # Example 1
assert Solution().minimumOperations([1,2,3,4]) == 3 # Example 2
# Edge cases
assert Solution().minimumOperations([1]) == 0 # Single element, already palindrome
assert Solution().minimumOperations([1,1,1,1]) == 0 # Already palindrome
assert Solution().minimumOperations([1,3,1]) == 0 # Already palindrome
assert Solution().minimumOperations([1,2]) == 1 # Needs one merge
assert Solution().minimumOperations([1,1,2,1,1]) == 1 # Middle merge
assert Solution().minimumOperations([1,2,3,2,1]) == 0 # Already palindrome
| Test | Why |
|---|---|
[4,3,2,1,2,3,1] |
Generic case with multiple merges needed |
[1,2,3,4] |
Case where multiple sequential merges collapse the array |
[1] |
Single element edge case |
[1,1,1,1] |
Already palindrome |
[1,2] |
Minimal array needing merge |
[1,1,2,1,1] |
Merge needed in the middle |
[1,2,3,2,1] |
Already palindrome with odd length |
Edge Cases
Single-element array: If nums has length 1, it is already