快速排序算法的最大和最小交换量

Posted

技术标签:

【中文标题】快速排序算法的最大和最小交换量【英文标题】:Maximum and minimum amont of swaps for Quicksort Algoritm 【发布时间】:2022-01-01 02:26:36 【问题描述】:

我的作业中有以下内容:

考虑快速排序算法。

我。举一个需要最大交换操作次数的 8 元素数组的例子 (假设枢轴是第一个元素),并解释你的答案。

二。举一个需要最少交换操作次数的 8 元素数组的例子(假设枢轴是最后一个元素),并解释你的答案。

我认为需要最少交换操作次数的数组是已经排序的数组 1,2,3,4,5,6,7,8,因为不会有任何交换。但是对于第一部分,除了编写代码和暴力破解之外,我不知道如何处理它。解决这个问题的正确方法是什么?

【问题讨论】:

您认为快速排序“最难”处理的值的布局是什么?考虑一个更简单的情况,例如只有 3 个值的数组(它足够小,您可以在论文中快速绘制所有可能的情况)。 @devouredelysium:IMO,3 个元素的情况太小了,看不到模式。 暴力破解有什么问题,并提供您的程序作为解释? @trincot 我没有程序我必须在纸上解决这个问题并解释我是如何做到的。 @trincot:这确实是可行的。 40230 个案例可供尝试。 【参考方案1】:

我会通过编程来做到这一点:

产生 [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

分区方案有很多变体,它们会影响最坏的情况。

【讨论】:

以上是关于快速排序算法的最大和最小交换量的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法之排序算法:交换排序

冒泡排序和快速排序的比较

排序算法之快速选择排序

排序算法总结

冒泡排序法

排序算法之交换排序(冒泡排序快速排序)