希尔排序需要和直接插入排序我们来联合起来进行比较。
1.直接插入排序
首先我们来看直接插入排序:
直接插入排序就是一个将无序区的内容向有序区放的一个过程,有序区不断地变大,无序区不断的变小,这样最后全部变为有序,就完成了直接插入排序的过程了。
算法步骤:
1)将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
2)从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
示例代码:
template<typename T>
void InsertSort(T* arr, int size)
{
int pos = 0;
for (int index = pos + 1; index < size; index++)
{
pos = index - 1;
T tmp = arr[index];
while (pos >= 0 && arr[pos] < tmp)
{
arr[pos + 1] = arr[pos];
pos -= 1;
}
arr[pos + 1] = tmp;
}
}
接下来我们首先来讨论下直接插入排序,对于直接插入排序而言最好的情况就是接近于有序的时候,这个时候我们再次进行直接插入排序的时候效率就会很高,而最差的时候莫过于逆序的时候,这个时候需要和有序区的元素比较很多次。所以为了解决这个问题,一个叫做希尔的人提出了希尔排序。
2.希尔排序
关于希尔排序,其实就可以理解为让直接插入排序最差的变得更加高效
希尔排序的思想就是一种类似的分治的思想,首先我们把一个序列分成多个模块,然后对每个模块中对应下标相同的元素进行排序,这样就可以达到最快速度的把大的放前面,小的放后面(如果是升序相反)。
所以希尔排序的实质是把序列变成接近有序的序列,然后进行一次直接插入排序。
代码实现:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cstdlib>
using namespace std;
//希尔排序是为了解决直接插入排序的最差情况的排序,直接插入排序的最好情况是近似有序,最差情况是逆序,希尔排序就是把最差情况变得接近有序。
//希尔排序将数组划分为几个块,然后对对应的快的下标求余相同的元素进行排序,能够快速达到把大的放前面,小的放后面(如果是升序相反)。
template<typename T>
void ShellSort(T *arr, int size)
{
int gap = size;
while (gap > 1)
{
//注意这里的增量每次取除3+1。
gap = gap / 3 + 1;
int pos = 0;
//在这里希尔排序实际操作是按照顺序方式操作,并不是预想的一个模块一个模块的。操作的时候是对gap中的每一个进行希尔排序完以后再到下一个gap进行希尔排序。
for (int index = pos + gap; index < size; index++)
{
pos = index - gap;
T tmp = arr[index];
while (pos >= 0&&arr[pos] < tmp)
{
arr[pos+gap] = arr[pos];
pos -= gap;
}
arr[pos+gap] = tmp;
}
}
}
希尔排序需要注意的是gap需要给一个大值。
3.总结
希尔排序是一种更加高效的插入排序,它和直接插入排序的时间复杂度虽然都在O(N)—O(N^2)之间,但是希尔排序来说是更加高效的,它的平均时间复杂度大约是O(N^1.3)。当希尔排序的最好情况对于直接插入排序而言就是最差情况,希尔排序的最差情况对于直接插入排序而言就是最好情况。