一,引言
哈希排序总的来说就是预排序+直接插入排序。本文将从下面几个方面进行讲解该排序算法。预排序,gap的选择,哈希排序的代码实现,时间复杂度,空间复杂度,稳定性等方面进行选择。
二,预排序
下面看一组例子:
当出现前半部分数字很大,如果直接进行插入排序算法会发现需要很多次操作,而我们的想要尽可能少得一个次数来实现大小数字的快速转换,于是就引入了预排序的概念。将一组数据分成(gap)组,相同组的数据为一组进行排序。
假设当前(gap) 为3时
三个数据为一组就产生了如下分组。
整个数组分为了如下三组:
(1):8------5------2;
(2):7------4------1;
(3):6--------------3;
1,(gap)等于3
这三组分别进行直接插入排序。
结果如下:
第一组排序结束------2-----5-----8;
第二组排序结束-------1------4-----7;
第三组排序结束----------3--------6;
会发现相对大的树快速的向后平移,而相对小的数排在了前面。此时第一次排序结束。
当(gap)等于2时
进行这个过程如图;
如图分成红色和蓝色两组:
红色:1-----5------6-------7
蓝色:2-----3-----4--------8
对这两组分别进行直接排序如下:
由于这个例子给的比较极端及红色组和蓝色组均为有序,综上该数组保持不变。
当(gap)等于1时:
只有一组就相当于直接插入排序。
由此大家可以看出来相对于直接插入排序,进行多次的预排序有这非常大的优越性,尤其是数据量大的时候。
三,gap的选择:
在(gap)选择上由最早的(2/N),现在实验表明gap第一次为(3/N+1)之后每一次进行除3加一的方式进行递减直到gap取值到1的时候进行直接插入排序宣布该排序结束。
三,代码实现:
1,单趟排序:
单趟排序就是直接插入排序,只不过是一组一组分别进行,要思考需要变量是控制组数也就是说,第几组在执行,其次是结束条件什么时候单组排序结束,什么时候整个数组的所有组都排序结束。
思考明白这几个问题就可以着手尝试进行代码的书写。
代码样例如下:
void ShellSort(int* p, int bit)
{
int gap = 2;
for (int i = gap; i < bit; i = i+gap)
{
int j = i;
int trm = p[j];
while (j > 0)
{
if (p[j - gap] < trm)
{
break;
}
p[j] = p[j - gap];
j = j - gap;
}
p[j] = trm;
}
}
当gap等于2时的单趟排序;
void ShellSort(int* p, int bit)
{
int gap = 3;
for (int m = 0; m < gap; m++)
{
for (int i = gap + m; i < bit; i = i + gap)
{
int j = i;
int trm = p[j];
while (j > 0)
{
if (p[j - gap] < trm)
{
break;
}
p[j] = p[j - gap];
j = j - gap;
}
p[j] = trm;
}
}
}
当gap等于3时的多趟排序。
void ShellSort(int* p, int bit)
{
for (int gap = 3; gap > 0; gap--)
{
for (int m = 0; m < gap; m++)
{
for (int i = gap + m; i < bit; i = i + gap)
{
int j = i;
int trm = p[j];
while (j > 0)
{
if (p[j - gap] < trm)
{
break;
}
p[j] = p[j - gap];
j = j - gap;
}
p[j] = trm;
}
}
}
}
第一个for循环控制gap
第二个for循环控制在相同gap的组数
第三个for循环控制相同gap相同组数的排序结束
void ShellSort(int* p, int bit)
{
for(int gap = bit/3 +1; gap > 0 ; gap = gap/3+1)
{
for (int m = 0; m < gap; m++)
{
for (int i = gap + m; i < bit; i = i + gap)
{
int j = i;
int trm = p[j];
while (j > 0)
{
if (p[j - gap] < trm)
{
break;
}
p[j] = p[j - gap];
j = j - gap;
}
p[j] = trm;
}
}
if (gap == 1)
{
break;
}
}
}
总体代码实现:
int main()
{
int arr[] = { 998756,8,7,678,8996,12,8098,6,5 ,43,1,2,3,4,5,43,54654,6};
ShellSort(arr, 18);
return 0;
}
测试样例
测试结果。
简化代码实现:
void ShellSort(int* a, int n)
{
int gap = n;
while (gap > 1)
{
// +1保证最后一个gap一定是1
// gap > 1时是预排序
// gap == 1时是插入排序
gap = gap / 3 + 1;
for (size_t i = 0; i < n - gap; ++i)
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
}
}
四,时间复杂度
大家只需要记一个大概的值在O(N^1.3)左右。
五,空间复杂度
由于没有开辟额外的空间,所有说空间复杂度为0(1)。
六,稳定性
相同数据在经过排序相对位置有没有发生变化为判断稳定性的依据,由于希尔排序会分成不同的组,所以说相同数据会发生变化,所以说希尔排序为不稳定性排序。