Skip to content

3510. Minimum Pair Removal to Sort Array II

Description

Given an array nums, you can perform the following operation any number of times:

  • Select the adjacent pair with the minimum sum in nums. If multiple such pairs exist, choose the leftmost one.
  • Replace the pair with their sum.

Return the minimum number of operations needed to make the array non-decreasing.

An array is said to be non-decreasing if each element is greater than or equal to its previous element (if it exists).

 

Example 1:

Input: nums = [5,2,3,1]

Output: 2

Explanation:

  • The pair (3,1) has the minimum sum of 4. After replacement, nums = [5,2,4].
  • The pair (2,4) has the minimum sum of 6. After replacement, nums = [5,6].

The array nums became non-decreasing in two operations.

Example 2:

Input: nums = [1,2,2]

Output: 0

Explanation:

The array nums is already sorted.

 

Constraints:

  • 1 <= nums.length <= 105
  • -109 <= nums[i] <= 109

 

Solutions

Solution: Doubly-Linked List + Priority Queue

  • Time complexity: O(nlogn)
  • Space complexity: O(n)

 

JavaScript

js
/**
 * @param {number[]} nums
 * @return {number}
 */
const minimumPairRemoval = function (nums) {
  const n = nums.length;
  const list = new DoublyLinked();
  const merged = Array.from({ length: n }, () => false);
  const minHeap = new PriorityQueue((a, b) => {
    if (a.cost === b.cost) return a.first.left - b.first.left;

    return a.cost - b.cost;
  });
  let decreaseCount = 0;
  let count = 0;

  list.insertLast(new Node(nums[0], 0));

  for (let i = 1; i < nums.length; i++) {
    list.insertLast(new Node(nums[i], i));

    const curr = list.tail();

    minHeap.enqueue({
      first: curr.getPrev(),
      second: curr,
      cost: nums[i] + nums[i - 1],
    });

    if (nums[i - 1] > nums[i]) {
      decreaseCount++;
    }
  }

  while (decreaseCount > 0) {
    const { first, second, cost } = minHeap.dequeue();

    if (merged[first.left] || merged[second.left] || first.value + second.value !== cost) {
      continue;
    }

    count++;

    if (first.value > second.value) {
      decreaseCount--;
    }

    const prev = first.getPrev();
    const next = second.getNext();

    if (prev) {
      if (prev.value > first.value && prev.value <= cost) {
        decreaseCount--;
      }
      if (prev.value <= first.value && prev.value > cost) {
        decreaseCount++;
      }

      minHeap.enqueue({
        first: prev,
        second: first,
        cost: prev.value + cost,
      });
    }

    if (next) {
      if (second.value > next.value && cost <= next.value) {
        decreaseCount--;
      }
      if (second.value <= next.value && cost > next.value) {
        decreaseCount++;
      }

      minHeap.enqueue({
        first,
        second: next,
        cost: cost + next.value,
      });
    }

    list.remove(second);
    first.value = cost;
    merged[second.left] = true;
  }

  return count;
};

class Node {
  constructor(value, left) {
    this.value = value;
    this.left = left;
    this.prev = null;
    this.next = null;
  }

  getPrev() {
    return this.prev;
  }

  getNext() {
    return this.next;
  }
}

class DoublyLinked {
  headNode = null;
  tailNode = null;
  size = 0;

  head() {
    return this.headNode;
  }

  tail() {
    return this.tailNode;
  }

  insertLast(node) {
    if (this.headNode) {
      node.prev = this.tailNode;
      this.tailNode.next = node;
      this.tailNode = node;
    } else {
      this.headNode = node;
      this.tailNode = node;
    }

    this.size++;
  }

  remove(node) {
    if (!node) return;

    const prev = node.prev;
    const next = node.next;

    if (prev) {
      prev.next = next;
    } else {
      this.headNode = next;
    }

    if (next) {
      next.prev = prev;
    } else {
      this.tailNode = prev;
    }

    node.prev = null;
    node.next = null;
    this.size--;
  }
}

Released under the MIT license