Guide contents

Lesson 12 of 164

Rearrange a Sorted Array in Max/Min Form

Given an array sorted in ascending order, rearrange it so that it alternates between the largest and smallest remaining values: first the maximum, then the minimum, then the second maximum, then the second minimum, and so on.

Example 1

Text
Input:  [1, 2, 3, 4, 5, 6]
Output: [6, 1, 5, 2, 4, 3]

Example 2

Text
Input:  [1, 2, 3, 4, 5]
Output: [5, 1, 4, 2, 3]
Explanation: with an odd length, the middle value ends up last.

Brute force: two pointers into a new array

lo points at the smallest unused value (the left end) and hi points one position past the largest unused value (the right end). Alternate between taking from hi and from lo until they meet.

Walkthrough with [1, 2, 3, 4, 5, 6]:

step take from lo hi (exclusive) result
1 6 right 0 5 [6]
2 1 left 1 5 [6, 1]
3 5 right 1 4 [6, 1, 5]
4 2 left 2 4 [6, 1, 5, 2]
5 4 right 2 3 [6, 1, 5, 2, 4]
6 3 left 3 3 [6, 1, 5, 2, 4, 3]
Java
import java.util.Arrays;

public class Main {
    static int[] maxMin(int[] nums) {
        int[] result = new int[nums.length];
        int lo = 0, hi = nums.length;
        boolean takeMax = true;
        int k = 0;

        while (lo < hi) {
            if (takeMax) {
                hi--;
                result[k++] = nums[hi];
            } else {
                result[k++] = nums[lo];
                lo++;
            }
            takeMax = !takeMax;
        }
        return result;
    }

    public static void main(String[] args) {
        System.out.println(Arrays.toString(maxMin(new int[]{1, 2, 3, 4, 5, 6})));
        System.out.println(Arrays.toString(maxMin(new int[]{1, 2, 3, 4, 5})));
    }
}
Output
[6, 1, 5, 2, 4, 3]
[5, 1, 4, 2, 3]

Time: O(n). Space: O(n) for the new array. Because hi is exclusive, nums[hi] is read only after hi is decremented, so the index is always valid.

This is the solution to give first. The follow-up question is usually: can you do it with O(1) extra space?

Optimal space: store two values in each slot

Writing the result into nums directly would overwrite values we still need. The trick is to make each slot hold both its old value and its new value for a while.

Pick m = max + 1, a number larger than every element. For any two values old and new, both between 0 and max, the number old + new * m encodes both:

  • (old + new * m) % m == old
  • (old + new * m) / m == new

So at index i, add new * m to the slot. Any slot that has already been updated still gives back its original value through % m. At the end, divide every slot by m to keep only the new values.

Walkthrough with [1, 2, 3, 4, 5, 6], m = 7:

i source (original value, via % m) slot becomes decoded new value
0 nums[5] = 6 1 + 6·7 = 43 6
1 nums[0] = 43 % 7 = 1 2 + 1·7 = 9 1
2 nums[4] = 5 3 + 5·7 = 38 5
3 nums[1] = 9 % 7 = 2 4 + 2·7 = 18 2
4 nums[3] = 18 % 7 = 4 5 + 4·7 = 33 4
5 nums[2] = 38 % 7 = 3 6 + 3·7 = 27 3

Dividing by 7 gives [6, 1, 5, 2, 4, 3].

Java
import java.util.Arrays;

public class Main {
    static void maxMin(long[] nums) {
        int n = nums.length;
        if (n == 0) {
            return;
        }
        long m = nums[n - 1] + 1; // larger than every value (the input is sorted)
        int lo = 0, hi = n - 1;

        for (int i = 0; i < n; i++) {
            long source;
            if (i % 2 == 0) {
                source = nums[hi];
                hi--;
            } else {
                source = nums[lo];
                lo++;
            }
            nums[i] += (source % m) * m; // `% m` recovers the original value
        }
        for (int i = 0; i < n; i++) {
            nums[i] /= m;
        }
    }

    public static void main(String[] args) {
        long[] a = {1, 2, 3, 4, 5, 6};
        long[] b = {0, 10, 20, 30, 40};
        maxMin(a);
        maxMin(b);
        System.out.println(Arrays.toString(a) + " " + Arrays.toString(b));
    }
}
Output
[6, 1, 5, 2, 4, 3] [40, 0, 30, 10, 20]

Time: O(n). Space: O(1).

This technique has two limits, and you should state them when you present it:

  • Values must be non-negative. % and / do not separate the two values correctly for negative numbers. For negative input, shift every value by -min first and shift back at the end.
  • Overflow. old + new * m can be close to max², so the code uses long. With int, the encoded value wraps silently once max passes about 46,000.

Edge cases

  • Empty array or one element: returned unchanged.
  • Duplicates: both approaches handle them.
  • Input not sorted: sort it first, which adds O(n log n).