Make Lexicographically Smallest Array by Swapping Elements

medium array connected components union-find sorting

Problem

You are given a 0-indexed array of positive integers nums and a positive integer limit. You may swap nums[i] and nums[j] any number of times whenever |nums[i] − nums[j]| ≤ limit. Return the lexicographically smallest array reachable.

Swappability is transitive, so the values split into connected components. Within one component the values can be placed in any order across that component's positions. To be smallest, give each component's earliest positions its smallest values.

Inputnums = [1,5,3,9,8], limit = 2
Output[1,3,5,8,9]
Components by value: {1}, {3,5} (positions 1,2), {8,9} (positions 3,4). Sort each component into its own positions.

def lexicographicallySmallestArray(nums, limit):
    n = len(nums)
    # indices sorted by their value
    order = sorted(range(n), key=lambda i: nums[i])
    ans = [0] * n
    i = 0
    while i < n:
        # grow one connected component of consecutive sorted values
        j = i + 1
        while j < n and nums[order[j]] - nums[order[j - 1]] <= limit:
            j += 1
        group = order[i:j]                  # original indices in this component
        pos = sorted(group)                 # positions, left to right
        vals = sorted(nums[k] for k in group)
        for p, v in zip(pos, vals):         # smallest value to earliest position
            ans[p] = v
        i = j
    return ans
function lexicographicallySmallestArray(nums, limit) {
  const n = nums.length;
  // indices sorted by their value
  const order = [...nums.keys()].sort((a, b) => nums[a] - nums[b]);
  const ans = new Array(n).fill(0);
  let i = 0;
  while (i < n) {
    // grow one connected component of consecutive sorted values
    let j = i + 1;
    while (j < n && nums[order[j]] - nums[order[j - 1]] <= limit) j++;
    const group = order.slice(i, j);          // original indices
    const pos = [...group].sort((a, b) => a - b);
    const vals = group.map(k => nums[k]).sort((a, b) => a - b);
    for (let t = 0; t < pos.length; t++) ans[pos[t]] = vals[t];
    i = j;
  }
  return ans;
}
int[] lexicographicallySmallestArray(int[] nums, int limit) {
    int n = nums.length;
    Integer[] order = new Integer[n];
    for (int k = 0; k < n; k++) order[k] = k;
    Arrays.sort(order, (a, b) -> nums[a] - nums[b]);   // indices by value
    int[] ans = new int[n];
    int i = 0;
    while (i < n) {
        int j = i + 1;                                 // grow a component
        while (j < n && nums[order[j]] - nums[order[j - 1]] <= limit) j++;
        int len = j - i;
        int[] pos = new int[len], vals = new int[len];
        for (int t = 0; t < len; t++) { pos[t] = order[i + t]; vals[t] = nums[order[i + t]]; }
        Arrays.sort(pos);                              // positions ascending
        Arrays.sort(vals);                             // values ascending
        for (int t = 0; t < len; t++) ans[pos[t]] = vals[t];
        i = j;
    }
    return ans;
}
vector<int> lexicographicallySmallestArray(vector<int>& nums, int limit) {
    int n = nums.size();
    vector<int> order(n);
    iota(order.begin(), order.end(), 0);
    sort(order.begin(), order.end(), [&](int a, int b){ return nums[a] < nums[b]; });
    vector<int> ans(n);
    int i = 0;
    while (i < n) {
        int j = i + 1;                                 // grow a component
        while (j < n && nums[order[j]] - nums[order[j - 1]] <= limit) j++;
        vector<int> pos(order.begin() + i, order.begin() + j);
        vector<int> vals;
        for (int k : pos) vals.push_back(nums[k]);
        sort(pos.begin(), pos.end());                  // positions ascending
        sort(vals.begin(), vals.end());                // values ascending
        for (int t = 0; t < (int)pos.size(); t++) ans[pos[t]] = vals[t];
        i = j;
    }
    return ans;
}
Time: O(n log n) Space: O(n)