Shift an array in O(n) in place
Below I copied the code for shifting an array in place.
void shift(Object[] array, int startIndexInclusive, int endIndexExclusive, int offset) {
if (array == null) {
return;
}
if (startIndexInclusive >= array.length - 1 || endIndexExclusive <= 0) {
return;
}
if (startIndexInclusive = array.length) {
endIndexExclusive = array.length;
}
int n = endIndexExclusive - startIndexInclusive;
if (n 0) {
int n_offset = n - offset;
if (offset > n_offset) {
swap(array, startIndexInclusive, startIndexInclusive + n - n_offset, n_offset);
n = offset;
offset -= n_offset;
} else if (offset < n_offset) {
swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset);
startIndexInclusive += offset;
n = n_offset;
} else {
swap(array, startIndexInclusive, startIndexInclusive + n_offset, offset);
break;
}
}
The swap(array, index1, index2, len)
method swaps in the given array the elements from [index1, index1 + len) with the ones [index2, index2 + len).
Even though it may seem complicated at first, the idea is pretty simple. If the offset is half of array length, or in other words if offset == (n – offset), where n is the total number of elements to be shifted, then the shift is equivalent of swapping the two halves of the array.
For the first two cases we swapped the portions at the ends and one of them will come in place and we will continue the iteration for the rest as shown in the below figure.
Space complexity is clearly O(1), but what about time complexity. I’m gonna prove that it is O(n).
Let Sh(n, k) be the problem of shifting k positions in an array of size n and Sw(k) be the problem of swapping k elements in an array. For the sake of simplicity I left out the start and end indices.
It is obvious that O(Sw(k)) = O(k).
It is also obvious that O(Sh(1, k)) = O(1), with k < 1. Also O(Sh(x, 0)) = O(1).
Now let's assume that O(Sh(n, k)) = O(n), whatever n, with k < n. I'll try to prove that O(Sh(n + 1, k')) = O(n), with k' < n + 1.
Analyzing the algorithm we have
O (Sh(n + 1, k’)) = max(
O(Sw(n + 1 – k’)) + O(Sh(k’, 2k’ – n – 1)), if 2k’ > n + 1
O(Sw(k’)) + O(Sh(n + 1 – k’, k’)), if 2k’ < n + 1
O(Sw(k')), if 2k' == n + 1
)
- First case 2k’ > n + 1
O(Sh(n + 1, k’)) = O(Sw(n + 1 – k’)) + O(Sh(k’, 2k’ – n – 1)) = O(n) + O(Sh(k’, 2k’ – n – 1))
Because (k’ < n +1) ⇒ (k' ≤ n) and (2k' ≤ 2n) ⇒ (2k' – n – 1 ≤ n – 1) ⇒ (2k' – n – 1 < n), then O (Sh(n + 1, k')) = O(n) + O(n) = O(n).
- Second case 2k’ < n + 1
O(Sh(n + 1, k')) = O(Sw(k')) + O(Sh(n + 1 – k', k')) = O(k') + O(Sh(n + 1 – k', k')) = O(n) + O(Sh(n + 1 – k', k'))
Because (0 < k') ⇒ (n + 1 – k' < n + 1) ⇒ (n + 1 – k' ≤ n) and (k' < (n + 1)/2) ⇒ (k' ≤ n/2) ⇒ (k' < n), then O(Sh(n + 1, k')) = O(n) + O(n) = O(n).
- Third case 2k’ == n + 1
O(Sh(n + 1, k’)) = O(Sw(k’)) = O(n)
So O(Sh(n + 1, k’)) = O(n). As a consequence O(Sh(n, k)) = O(n), whatever n, with k < n.
The code will become part of commons-lang, ArrayUtils class, as of version 3.5.