I'm currently solving LeetCode problem 2161. Partition Array According to Given Pivot:
You are given a 0-indexed integer array
numsand an integerpivot. Rearrangenumssuch that the following conditions are satisfied:
Every element less than
pivotappears before every element greater thanpivot.Every element equal to
pivotappears in between the elements less than and greater thanpivot.The relative order of the elements less than
pivotand the elements greater thanpivotis maintained.
- More formally, consider every
pi,pjwherepiis the new position of theithelement andpjis the new position of thejthelement. For elements less thanpivot, ifi < jandnums[i] < pivotandnums[j] < pivot, thenpi < pj. Similarly for elements greater thanpivot, ifi < jandnums[i] > pivotandnums[j] > pivot, thenpi < pj.Return
numsafter the rearrangement.Example:
Input:
nums = [9,12,5,10,14,3,10],pivot = 10
Output:[9,5,3,10,10,12,14]
A solution with O(n) memory is trivial. I tried to come up with a O(1) memory solution and approached a similar to insertion sort way:
public int[] pivotArray(int[] nums, int pivot) {
for(int i = 0; i < nums.length; i++){
if(nums[i] < pivot){
for(int j = i; j > 0 && nums[j - 1] >= pivot; j--){
int tmp = nums[j];
nums[j] = nums[j - 1];
nums[j - 1] = tmp;
}
}
if(nums[i] == pivot){
for(int j = i; j > 0 && nums[j - 1] > pivot; j--){
int tmp = nums[j];
nums[j] = nums[j - 1];
nums[j - 1] = tmp;
}
}
}
return nums;
}
The issue is the solution has O(n^2) time complexity. Is there an algorithm to solve this faster than O(n^2)? I'm not sure if O(n) time and O(1) space complexity is possible though.
With a slight variation on the answer of Matt Timmermans you could first remove the pivot values from the input array (packing the other values to the left side of it), then apply a O(𝑛log𝑛) divide-and-conquer algorithm to repeatedly merge two subarrays that are already partitioned by the pivot, rotating the center two partitions (of the four partitions). Then finally insert the removed pivot values back between the two partitions that remain.
To avoid consuming O(log𝑛) stack space, perform the divide-and-conquer algorithm in a bottom-up fashion (no recursion).
Here is an implementation:
class Solution {
void sliceRotateLeft(int[] arr, int start, int end, int shift) {
// Rotation in O(𝑛), where 𝑛 is the size of the array slice
int size = end - start;
if (size <= 1) return;
shift %= size;
if (shift == 0) return;
for (int count = 0, first = 0; count < size; first++, count++) {
int j = first;
int value = arr[start + j];
for (int i = j + shift; i != first; j = i, i = (i + shift) % size) {
arr[start + j] = arr[start + i];
count++;
}
arr[start + j] = value;
}
}
int sliceFindPivotIndex(int[] arr, int start, int end, int pivot) {
// Linear search. NB: binary search would be possible,
// but does not improve overall complexity.
while (start < end && arr[start] < pivot) start++;
return start;
}
public int[] pivotArray(int[] nums, int pivot) {
// Phase 1: pack all non-pivot values to the left
int len = 0; // Number of non-pivot values
for (int i = 0; i < nums.length; i++) {
if (nums[i] != pivot) nums[len++] = nums[i];
}
// Phase 2: partition non-pivot values into less-than, greater-than
// Use bottom up divide-and-conquer
for (int size = 1; size < len; size *= 2) {
// Merge two correctly partitioned chunks into one combined chunk
for (int start = 0; start + size < len; start += size * 2) {
// Get the offsets of the partitioned subarrays
int mid = start + size;
int end = Math.min(mid + size, len);
int left = sliceFindPivotIndex(nums, start, mid, pivot);
int right = sliceFindPivotIndex(nums, mid, end, pivot);
sliceRotateLeft(nums, left, right, mid - left);
}
}
// Phase 3: inject the pivot values
int pivotFrequency = nums.length - len;
int i;
for (i = len - 1; i >= 0 && nums[i] > pivot; i--) {
nums[i + pivotFrequency] = nums[i];
}
for (i++; pivotFrequency > 0; i++, pivotFrequency--) {
nums[i] = pivot;
}
return nums;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With