【问题标题】:number of swaps and comparisons in bubble, selection, insertion and quick sorts冒泡、选择、插入和快速排序中的交换和比较次数
【发布时间】:2017-08-06 06:07:31
【问题描述】:

我已经用 Java 实现了所有四种排序算法。只是为了它,我决定查看每个算法中的交换次数和比较。对于大小为 20 的随机数组,这是我的结果

冒泡排序:87 次交换,87 次比较

插入排序:87 次交换,87 次比较

选择排序:19 次交换,29 次比较

快速排序:11940 次交换,我什至不知道从哪里计算比较

为什么冒泡排序和选择排序相同?我的意思是看代码我几乎可以看到它。循环几乎相同,我只是希望有人为我指出。

我明白为什么选择排序的交换次数最少

快速排序对我来说是个谜(该死的递归)。我认为这就是为什么交换的数量是疯狂的。为什么我的实施如此缓慢?其他三个在它之前完成。

***** 代码 - 如果有任何遗漏请告诉我 *******

Swap 对于所有三个实现都是相同的

private void swap(int firstIndex, int secondIndex) {
    int temp = array[firstIndex];
    array[firstIndex] = array[secondIndex];
    array[secondIndex] = temp;
}

气泡

public void sort() {
    for(int out = nElements-1; out > 1; out--) {// outer backwards loop
        for(int in = 0; in < out; in++) { // inner forward loop 
            if(array[in] > array[in+1]) {
                swap(in, in + 1);
                totalSwaps++;
                totalComparisons++;
            }
        }
    }
}

选择

public void sort() {
    for (int i = 0; i < nElements-1; i++) {
        int currentMinIndex = i;
        for (int j = i + 1; j < nElements; j++)
            if (array[j] < array[currentMinIndex]) {
                currentMinIndex = j;
                totalComparisons++;
            }
        swap(i, currentMinIndex);
        totalSwaps++;
    }
}

插入

public void sort() {
    for(int i = 1; i < nElements; i++) {
        for(int j = i; j > 0; j--) {
            if(array[j] < array[j-1]) {
                swap(j, j - 1);
                totalSwaps++;
                totalComparisons++;
            }
        }
    }
}

快速排序

public void sort() {
    sort(this.array, 0, array.length - 1);
}
private void sort(int[] array, int left, int right) {
    if(left >= right)
        return;

    int randomIndex = new Random().nextInt(array.length);
    int pivot = array[randomIndex];

    // returns the dividing point between the left side and the right side
    int index = partition(array, left, right, pivot);

    sort(array, left, index - 1);
    sort(array, index, right);
}

private int partition(int[] array, int left, int right, int pivot) {
    while(left <= right) {
        while(array[left] < pivot)  // will break when there's an element to the left of the pivot that's greater
            left++;

        while(array[right] > pivot)  // breaks when there's an element to the right of the pivot that's less
            right--;

        if(left <= right) {
            swap(left, right);
            left++;
            right--;
            totalSwaps++;

        }

    }

    return left;  // left will be at the partition point
}

主要

import java.util.Random;


public class Sorting {

private static final int maxSize = 50;
private static int[] randomArray() {
    int[] array = new int[maxSize];
    Random random = new Random();
    random.setSeed(0);
    for(int i = 0; i < maxSize; i++)
        array[i] = random.nextInt(50);
    return array;
}

public static void main(String[] args) {

    int[] randomArr = randomArray();

    BubbleSort bubbleSort = new BubbleSort(randomArr);
    bubbleSort.display();
    bubbleSort.sort();
    bubbleSort.display();

    randomArr = randomArray();

    SelectionSort selectionSort = new SelectionSort(randomArr);
    selectionSort.sort();
    selectionSort.display();

    randomArr = randomArray();

    InsertionSort insertionSort = new InsertionSort(randomArr);
    insertionSort.sort();
    insertionSort.display();

    System.out.println("Bubble Sort: Swaps = " + bubbleSort.totalSwaps + " Comparisons = " + bubbleSort.totalComparisons);
    System.out.println("Selection Sort: Swaps = " + selectionSort.totalSwaps + " Comparisons = " + selectionSort.totalComparisons);
    System.out.println("Insertion Sort: Swaps = " + insertionSort.totalSwaps + " Comparisons = " + insertionSort.totalComparisons);


    randomArr = randomArray();

    QuickSort quickSort = new QuickSort(randomArr);
    quickSort.sort();
    quickSort.display();

    System.out.println("Quick Sort: Swaps = " + quickSort.totalSwaps);
}

}

输出

10 48 29 47 15 3 41 11 19 4 27 27 23 12 45 44 34 25 41 20  // original
3 4 10 11 12 15 19 20 23 25 27 27 29 34 41 41 44 45 47 48  // bubble
3 4 10 11 12 15 19 20 23 25 27 27 29 34 41 41 44 45 47 48  // selection
3 4 10 11 12 15 19 20 23 25 27 27 29 34 41 41 44 45 47 48  // insertion
Bubble Sort: Swaps = 87 Comparisons = 87
Selection Sort: Swaps = 19 Comparisons = 29
Insertion Sort: Swaps = 87 Comparisons = 87
3 4 10 11 12 15 19 20 23 25 27 27 29 34 41 41 44 45 47 48  // quick
Quick Sort: Swaps = 25283

【问题讨论】:

  • 你的比较计数全错了。您应该计算执行的比较次数,但您计算的是评估为true的次数。
  • 此外,您的插入排序实现有点错误——尽管它会正确排序,但它执行的比较比标准插入排序执行的要多(并且不计算任何额外内容)。跨度>

标签: java quicksort bubble-sort insertion-sort selection-sort


【解决方案1】:

至于如何计算操作,您总是可以考虑添加一个间接层。例如,使用这样的类来执行和计数操作:

class Instrumentation {
    int compares = 0;
    int swaps = 0;

    boolean compareLess(int left, int right) {
        compares++;
        return left < right;
    }

    boolean compareGreater(int left, int right) {
        compares++;
        return left > right;
    }

    void swap(int[] array, int index1, int index2) {
        int temp = array[index1];

        array[index1] = array[index2];
        array[index2] = temp;

        swaps++;
    }

    void printResult(String label) {
        System.out.print(label);
        System.out.print(": Swaps = ");
        System.out.print(swaps);
        System.out.print(" Comparisons = ");
        System.out.println(compares);
    }
}

修改您的代码以使用该检测类来计算操作,我得到了以下结果:

Original data:
10 48 29 47 15 3 41 11 19 4 27 27 23 12 45 44 34 25 41 20 

BubbleSort: Swaps = 87 Comparisons = 189
3 4 10 11 12 15 19 20 23 25 27 27 29 34 41 41 44 45 47 48 

SelectionSort: Swaps = 19 Comparisons = 190
3 4 10 11 12 15 19 20 23 25 27 27 29 34 41 41 44 45 47 48 

InsertionSort: Swaps = 87 Comparisons = 190
3 4 10 11 12 15 19 20 23 25 27 27 29 34 41 41 44 45 47 48 

QuickSort: Swaps = 3221 Comparisons = 110575
3 4 10 11 12 15 19 20 23 25 27 27 29 34 41 41 44 45 47 48 

现在观察比较排序的主要特征是,在最坏的情况下,它们涉及将每个元素与其他元素进行比较。对于 20 个元素,即 20 * 19 / 2 = 190 次比较,这基本上是您的比较排序实现在每种情况下产生的结果(对于冒泡排序,则更少)。

这实际上是您在每种情况下对冒泡和选择排序的期望,但不是在平均情况下您对插入排序的期望。那是因为你的插入排序实现是有缺陷的:这种排序的前提是它依赖于它的中间结果(将数组的第一部分按顺序排列)来减少所需的比较次数。内部循环中的比较第一次失败,因此不需要交换,您应该从(内部)循环中中断,因为您知道在外部循环的下一次迭代之前不需要进一步的交换。实施该措施可将您的特定数据的比较次数减少到 105 次。

比较排序之间的交换次数也很有意义:冒泡排序和插入排序都通过与相邻元素的一系列交换将每个元素从其初始位置移动到最终位置。实际上,您的实现实际上是彼此的镜像。但是,我不准备超越这一挥手的实际证据。

对于选择排序,是的,对于 n 个元素的排序,它总是执行 (n * (n - 1)) / 2 次比较, 最多 n - 1 次交换(如果您执行并计算自交换,则正好是 n - 其中 1 次)。

然后是您的快速排序。显然它不是很快——它有一些非常错误的地方。更多的检测告诉我,它下降到 far 的递归深度太大(平均约为 400,而即使在最坏的情况下也不应该超过 n)。问题在于您的随机枢轴选择。不是从被排序的子数组中选择一个枢轴,而是从整个数组中选择它。要解决这个问题,请替换

int randomIndex = new Random().nextInt(array.length);

int randomIndex = left + new Random().nextInt(right + 1 - left);

这应该会为您提供更有利的比较和交换计数,但在您开始对更大的数组进行排序之前,您不会真正注意到快速排序提供了多少优势。

您可以采取更多措施来改进您的 QuickSort 实施,但我没有看到任何其他真正错误。

【讨论】:

  • 非常感谢约翰。这很有帮助。
猜你喜欢
  • 2019-05-10
  • 2013-03-10
  • 2018-10-01
  • 1970-01-01
  • 2018-05-10
  • 1970-01-01
  • 2011-11-30
  • 2012-08-26
  • 2015-04-25
相关资源
最近更新 更多