We use cookies (including Google cookies) to personalize ads and analyze traffic. By continuing to use our site, you accept our Privacy Policy.

Largest Element in an Array after Merge Operations

Number: 2872

Difficulty: Medium

Paid? No

Companies: Amazon


Problem Description

You are given a 0-indexed array nums of positive integers. You may repeatedly perform the following operation: choose an index i (with 0 ≤ i < nums.length – 1) such that nums[i] ≤ nums[i+1]. Then replace nums[i+1] with nums[i] + nums[i+1] and remove nums[i] from the array. Your goal is to obtain as large an element as possible in the final array. Return the value of the largest element that can eventually occur.

For example, for nums = [2,3,7,9,3] one optimal sequence of operations is: • Merge at i = 0 → [5,7,9,3] • Merge at i = 1 → [5,16,3] • Merge at i = 0 → [21,3] Thus, 21 is the answer.


Key Insights

  • The only allowed merge is between two adjacent numbers when the left is no larger than the right.
  • Every merge “combines” a contiguous block of numbers; the merged value is the sum of that block.
  • Although many merge orders are possible, the final answer is the maximum over all contiguous segments that can be merged (in some legal order).
  • A final (non‐mergeable) configuration is equivalent to a partition of the array into segments that were “fully merged” (each merge performed only when the left segment’s value was not greater than the right’s).
  • A greedy approach that always picks the rightmost merge candidate can “delay” merging on the left – effectively allowing later merges to “inherit” a larger accumulated sum.
  • To implement an optimal (greedy) simulation we can maintain a doubly‐linked list of values along with a “max‐heap” (priority queue) of valid merge candidates (each candidate corresponds to a node whose value is ≤ the value of its next neighbor). Then, repeatedly pick the candidate with the highest index (i.e. the rightmost candidate) to merge. After a merge, update the linked list connections and check whether new merge opportunities have appeared with the neighbor to the left or with the new right neighbor.

Space and Time Complexity

Time Complexity: O(n log n) in the worst case – each merge (of which there are O(n)) performs O(log n) work due to heap updates. Space Complexity: O(n) – for the linked list nodes and the auxiliary heap.


Solution

We simulate the merge operations optimally using a doubly‐linked list so that deleting an element is O(1) and the “neighbors” are always available to update. To always “take” the best opportunity we use a max‐heap keyed by the index of the left node in a valid adjacent merge (i.e. where node.val ≤ node.next.val). (By “max‐heap” the candidate with the highest index – the rightmost valid merge – is processed first.) Each time we remove the right node from the list after merging and update the value of the left node to the sum. Then we check if merging is now possible with the left’s previous neighbor or with its new next neighbor, and if so, add them to the heap. While doing so we keep track of the maximum value ever seen. This maximum will be our answer.

The following code solutions in Python, JavaScript, C++, and Java implement this strategy with detailed comments.


Code Solutions

# Define the Node class to represent each element in the doubly linked list.
class Node:
    def __init__(self, val):
        self.val = val          # current value of the node (may be a merged sum)
        self.prev = None        # pointer to the previous node
        self.next = None        # pointer to the next node

def largestMerge(nums):
    # Base case: if the array is empty, return 0.
    if not nums:
        return 0
    # Build a doubly linked list from the input array.
    nodes = [] 
    head = Node(nums[0])
    nodes.append(head)
    prev = head
    for num in nums[1:]:
        node = Node(num)
        node.prev = prev     # set the previous pointer
        prev.next = node     # update the next pointer of the previous node
        prev = node
        nodes.append(node)
    # To help with ordering in the heap, assign an index to each node.
    for i, node in enumerate(nodes):
        node.idx = i

    import heapq
    heap = []  # we will use Python’s heapq (min-heap) by pushing (-idx, node) to get a max-heap by index

    # For every adjacent pair (node and node.next), if the merge is legal then push it into the heap.
    for node in nodes:
        if node.next and node.val <= node.next.val:
            heapq.heappush(heap, (-node.idx, node))
    
    max_val = max(nums)  # record the maximum single element seen so far

    # Process merges until no valid merge is available.
    while heap:
        neg_idx, left = heapq.heappop(heap)
        right = left.next
        # It is possible that the linked list has changed; verify the merge is still valid.
        if not right or left.val > right.val:
            continue
        # Merge: update left’s value to be the sum of left and right.
        merged = left.val + right.val
        left.val = merged
        max_val = max(max_val, merged)
        # Remove the right node from the linked list.
        nxt = right.next
        left.next = nxt
        if nxt:
            nxt.prev = left
        # After merging, check the new pair (left, left.next). If legal, add to the heap.
        if left.next and left.val <= left.next.val:
            heapq.heappush(heap, (-left.idx, left))
        # Also check the neighbor to the left (if any) merging with the current left.
        if left.prev and left.prev.val <= left.val:
            heapq.heappush(heap, (-left.prev.idx, left.prev))
    return max_val

# Example usage:
if __name__ == '__main__':
    nums1 = [2, 3, 7, 9, 3]
    print(largestMerge(nums1))  # Expected output: 21
    nums2 = [5, 3, 3]
    print(largestMerge(nums2))  # Expected output: 11
← Back to All Questions