我会通过编程来做到这一点:
- 产生 [1,2,3,4,5,6,7,8] 的所有可能排列
- 计算需要多少交换才能对它们进行排序
- 保留一个使计数最大化的人
此外,您还可以对该结果再次执行排序,并输出所有已进行的交换。
结果将取决于分区算法。下面是 JavaScript 中的一个实现,它为 Lomuto 分区方案和 Hoare 分区方案执行上述算法(两者都被更改为以左值作为枢轴)。
你可以在这里运行它:
霍尔
function quickSortHoare(arr, start=0, end=arr.length, verbose=false) {
if (verbose) console.log("quickSortHoare(" + arr.map((val, i) => i >= start && i < end ? val : "-").join(" ") + ")");
let count = 0;
if (end - start < 2) return 0;
let pivot = arr[start];
let i = start - 1, j = end;
while (true) {
do { i++ } while (arr[i] < pivot);
do { j-- } while (arr[j] > pivot);
if (i >= j) break;
if (verbose) console.log(" swap values " + arr[i] + " and " + arr[j]);
[arr[i], arr[j]] = [arr[j], arr[i]];
count++;
}
if (verbose) console.log(" after partitioning: " + arr.map((val, i) => i >= start && i < end ? val : "-").join(" "));
if (i == start) i++;
return count + quickSortHoare(arr, start, i, verbose) + quickSortHoare(arr, i, end, verbose);
}
function* permutations(arr) {
if (arr.length <= 1) yield arr;
else {
let value = arr[0];
for (let perm of permutations(arr.slice(1))) {
for (let i = 0; i < arr.length; i++) {
yield perm.slice(0, i).concat(value, perm.slice(i));
}
}
}
}
function findWorstCase(sorter) {
let arr = [1, 2, 3, 4, 5, 6, 7, 8];
let worstCase;
let maxCount = -1;
for (let perm of permutations(arr)) {
let count = sorter([...perm]);
if (count > maxCount) {
maxCount = count;
worstCase = perm;
}
}
console.log(...worstCase);
sorter(worstCase, 0, worstCase.length, true);
console.log(maxCount + " swaps");
}
console.log("Hoare partitioning:");
findWorstCase(quickSortHoare);
发现的结果是,使用Hoare partition scheme 和最左边的值作为枢轴对 [5, 6, 8, 7, 2, 1, 4, 3] 进行排序需要 11 次交换:
Hoare partitioning:
5 6 8 7 2 1 4 3
quickSortHoare(5 6 8 7 2 1 4 3)
swap values 5 and 3
swap values 6 and 4
swap values 8 and 1
swap values 7 and 2
after partitioning: 3 4 1 2 7 8 6 5
quickSortHoare(3 4 1 2 - - - -)
swap values 3 and 2
swap values 4 and 1
after partitioning: 2 1 4 3 - - - -
quickSortHoare(2 1 - - - - - -)
swap values 2 and 1
after partitioning: 1 2 - - - - - -
quickSortHoare(1 - - - - - - -)
quickSortHoare(- 2 - - - - - -)
quickSortHoare(- - 4 3 - - - -)
swap values 4 and 3
after partitioning: - - 3 4 - - - -
quickSortHoare(- - 3 - - - - -)
quickSortHoare(- - - 4 - - - -)
quickSortHoare(- - - - 7 8 6 5)
swap values 7 and 5
swap values 8 and 6
after partitioning: - - - - 5 6 8 7
quickSortHoare(- - - - 5 6 - -)
after partitioning: - - - - 5 6 - -
quickSortHoare(- - - - 5 - - -)
quickSortHoare(- - - - - 6 - -)
quickSortHoare(- - - - - - 8 7)
swap values 8 and 7
after partitioning: - - - - - - 7 8
quickSortHoare(- - - - - - 7 -)
quickSortHoare(- - - - - - - 8)
11 swaps
洛穆托
这里是同样的东西,但使用基于 Lomuto 的分区:
function quickSortLomuto(arr, start=0, end=arr.length, verbose=false) {
if (verbose) console.log("quickSortLomuto(" + arr.map((val, i) => i >= start && i < end ? val : "-").join(" ") + ")");
let count = 0;
if (end - start < 2) return 0;
let pivot = arr[start];
let i = end;
for (let j = end - 1; j >= start; j--) {
if (arr[j] >= pivot) {
i--;
if (i != j) {
if (verbose) console.log(" swap values " + arr[i] + " and " + arr[j]);
[arr[i], arr[j]] = [arr[j], arr[i]];
count++;
}
}
}
if (verbose) console.log(" after partitioning: " + arr.map((val, i) => i >= start && i < end ? val : "-").join(" "));
return count + quickSortLomuto(arr, start, i, verbose) + quickSortLomuto(arr, i + 1, end, verbose);
}
function* permutations(arr) {
if (arr.length <= 1) yield arr;
else {
let value = arr[0];
for (let perm of permutations(arr.slice(1))) {
for (let i = 0; i < arr.length; i++) {
yield perm.slice(0, i).concat(value, perm.slice(i));
}
}
}
}
function findWorstCase(sorter) {
let arr = [1, 2, 3, 4, 5, 6, 7, 8];
let worstCase;
let maxCount = -1;
for (let perm of permutations(arr)) {
let count = sorter([...perm]);
if (count > maxCount) {
maxCount = count;
worstCase = perm;
}
}
console.log(...worstCase);
sorter(worstCase, 0, worstCase.length, true);
console.log(maxCount + " swaps");
}
console.log("Lomuto partitioning:");
findWorstCase(quickSortLomuto);
结果是最坏的情况 [2, 4, 6, 8, 7, 5, 3, 1] 需要 16 次交换:
Lomuto partitioning:
2 4 6 8 7 5 3 1
quickSortLomuto(2 4 6 8 7 5 3 1)
swap values 1 and 3
swap values 1 and 5
swap values 1 and 7
swap values 1 and 8
swap values 1 and 6
swap values 1 and 4
swap values 1 and 2
after partitioning: 1 2 4 6 8 7 5 3
quickSortLomuto(1 - - - - - - -)
quickSortLomuto(- - 4 6 8 7 5 3)
swap values 3 and 5
swap values 3 and 7
swap values 3 and 8
swap values 3 and 6
swap values 3 and 4
after partitioning: - - 3 4 6 8 7 5
quickSortLomuto(- - 3 - - - - -)
quickSortLomuto(- - - - 6 8 7 5)
swap values 5 and 7
swap values 5 and 8
swap values 5 and 6
after partitioning: - - - - 5 6 8 7
quickSortLomuto(- - - - 5 - - -)
quickSortLomuto(- - - - - - 8 7)
swap values 7 and 8
after partitioning: - - - - - - 7 8
quickSortLomuto(- - - - - - 7 -)
quickSortLomuto(- - - - - - - -)
16 swaps
分区方案有很多变体,它们会影响最坏的情况。