Перестановка, определяемая order
, состоит из одного или нескольких циклов. Применять один цикл к массиву a
несложно, но задача состоит в том, чтобы каким-то образом узнать, какие элементы массива принадлежат циклу, который вы уже обработали таким образом. Если есть способ пометить посещенных элементов, например, с помощью дополнительного бита, то эта проблема решена. Но использование дополнительного бита является сокрытием для массива с дополнительными данными. Так что это должно быть исключено.
Когда нет возможности выполнить такую маркировку, тогда все еще есть выход: выполнять операцию цикла над массивом a
только когда вы находитесь в крайнем левом индексеэтот цикл (или самый правый). Недостатком является то, что в каждом индексе вам нужно пройти цикл, к которому принадлежит индекс, чтобы проверить, действительно ли вы находитесь в его левом крайнем положении или нет. Это означает, что вы будете повторять один и тот же цикл несколько раз.
Вот как это выглядит в JavaScript:
function isLeftOfCycle(order, i) {
let j = order[i];
while (j > i) {
j = order[j];
}
return (j === i); // a boolean
}
function applyCycle(arr, order, i) {
let temp = arr[i];
let k = i;
let j = order[i];
while (j > i) {
arr[k] = arr[j];
k = j;
j = order[j];
}
arr[k] = temp;
}
function sort(a, order) {
for (let i = 0; i < order.length; i++) {
if (isLeftOfCycle(order, i)) {
applyCycle(a, order, i);
}
}
}
// Example run:
let a = [5, 35, 7, 2, 7];
let order = [3, 0, 2, 4, 1];
sort(a, order);
console.log(a);
Очевидно, что это имеет цену: сложность времени больше не O (n) , а O (n²) .