龙空技术网

歪说基础算法6-2:希尔排序:高速公路上的插入排序

猫道看球 107

前言:

现在同学们对“排序算法关键字是什么”都比较着重,看官们都需要学习一些“排序算法关键字是什么”的相关文章。那么小编同时在网上收集了一些有关“排序算法关键字是什么””的相关内容,希望姐妹们能喜欢,朋友们一起来了解一下吧!

如果插入排序(歪说基础算法6-1:魔法系列:我们一起搅动魔法锅,揭秘插入排序!)是我们的乌龟,那希尔排序就是兔子。尽管乌龟和兔子都属于同一类动物(排序算法),但它们的速度差别却大如天堂地狱之别。希尔排序是插入排序的一个版本,但它使用了一个巧妙的技巧来大大提高了排序速度。

魔法原理:什么是希尔排序?

希尔排序,也被称为“递减增量排序”,是由Donald Shell在1959年发明的。它的基本思想是对输入数组进行预排序,然后使用插入排序进行最后的排序。预排序是怎么进行的呢?它将数组分割成几个小的子序列,这些子序列是原数组的一部分,但它们的元素是原数组的几个特定间隔的元素。然后,对每个子序列进行插入排序。

你可能会问:“为什么要预排序?”好问题!预排序的目的是使数组“几乎有序”。在“几乎有序”的数组中,插入排序的性能可以达到接近线性时间。现在,你可能想知道,“几乎有序”是什么意思。好吧,当一个数组中任意i位置的元素大约在其最终排序位置的常数距离内时,我们就说这个数组是“几乎有序”的。

魔法实践:用案例理解希尔排序

让我们继续使用我们的魔法卡片。假设我们有一个乱序的魔法卡片数组[5, 3, 4, 7, 2, 8, 6, 9],我们要使用希尔排序对其进行排序。

我们首先选择一个增量,这个增量是我们划分子序列的步长。让我们选择增量为3,那么我们就有三个子序列:[5, 7, 6],[3, 2, 9]和[4, 8]。

我们对每个子序列进行插入排序。排序后的数组变为[3, 2, 4, 5, 7, 8, 6, 9]。

然后我们减小增量,让我们选择增量为2,那么我们就有两个子序列:[3, 4, 7, 6]和[2, 5, 8, 9]。我们再次进行插入排序,排序后的数组变为[2, 4, 3, 6, 5, 8, 7, 9]。

最后,我们将增量减小到1,即,我们只有一个子序列,它包含了所有的元素。我们再次进行插入排序,最终得到的数组为[2, 3, 4, 5, 6, 7, 8, 9]。这就是我们想要的结果!我们成功地使用希尔排序对魔法卡片进行了排序!

希尔排序的伪代码如下:

function shellSort(array)    gap = array.length / 2    while gap > 0        for i = gap to array.length            temp = array[i]            j = i            while j >= gap and array[j - gap] > temp                array[j] = array[j - gap]                j = j - gap            array[j] = temp        gap = gap / 2

希尔排序的时间复杂度是O(n^(3/2)),当n非常大时,其性能优于插入排序。尽管希尔排序在最坏的情况下可能需要进行多次的插入排序,但由于增量的选择和预排序的影响,实际上希尔排序通常要快得多。

希尔排序和插入排序的关键区别在于,希尔排序首先对间隔较大的元素进行排序,这样可以移动许多元素到正确的位置,大大减少了后续插入排序的工作量。最后当增量减少到1时,数组已经几乎有序,这时插入排序也会非常快。

那么,希尔排序是否就是最好的排序算法呢?不,不是的。尽管希尔排序在某些情况下可能非常高效,但它也有其局限性。例如,希尔排序并不稳定,也就是说,如果原数组中有两个相同的元素,希尔排序可能会改变它们的相对顺序。此外,希尔排序的时间复杂度也没有像快速排序或归并排序那样达到O(n log n)。

好了,下一步我们将实现一个希尔排序的C++程序。

#include <iostream>#include <vector>void shellSort(std::vector<int>& arr) {    int n = arr.size();    // Start with a big gap, then reduce the gap    for (int gap = n/2; gap > 0; gap /= 2) {        // Do a gapped insertion sort for this gap size.        for (int i = gap; i < n; i += 1) {            // add arr[i] to the elements that have been gap sorted            int temp = arr[i];            int j;                     // shift earlier gap-sorted elements up until the correct location for arr[i] is found            for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {                arr[j] = arr[j - gap];            }                // put temp (the original arr[i]) in its correct location            arr[j] = temp;        }    }}int main() {    std::vector<int> arr = {5, 3, 4, 7, 2, 8, 6, 9};    shellSort(arr);    for(auto val : arr){        std::cout << val << " ";    }    return 0;}

这段代码首先定义了一个shellSort函数,该函数接受一个int类型的vector作为输入,并对其进行希尔排序。这个函数首先计算出一个gap值,然后对数组进行分组,对每个组进行插入排序。随着gap值的减小,数组的有序度逐渐提高,最后当gap为1时,我们就完成了整个数组的排序。

在main函数中,我们创建了一个待排序的int类型的vector,然后调用了shellSort函数进行排序。排序结束后,我们遍历这个vector并打印出每个元素,以验证排序结果。

这就是希尔排序的C++实现,希望你能从中学到一些新知识。

标签: #排序算法关键字是什么