一文带你读懂排序算法(二):希尔排序算法
上文我们一起学习了解了3种基础的简单排序算法(冒泡算法/简单选择排序算法/快速插入排序算法),这三种算法简单归纳是:
冒泡:通过元素之间的全量比较,达到排序的目的;
简单插入排序算法:通过组合元素为小序列,通过比较将元素插入到特定位置;
快速选择排序:全量比较抽出最大值或最小值,放到数组的开头或者末尾。
以上的算法,在数据量较小且数据大部分有序的情况下,性能可以表现优异;但在生产环境下,面临的往往是杂乱无章且数据量极大的场景,这时候如果继续依赖基础算法,会导致效率低下,或给服务器性能带来较大的压力。所以需要更有效的算法,来帮助我们解决在大数量场景的问题。
下文一起学习下“插入排序”的优化算法 -- “希尔排序”。
背景
基本思想
第1趟排序
第2趟排序
第3趟排序
Demo
/**
* <p>
* 希尔排序算法
* 希尔排序是1959年,Shell发明的,这是第一个突破O(n2)的排序算法,他与直接插入排序不同的是,他会优先比较距离较近的元素。因此,希尔排序又叫做缩小增量排序。当步长为1时,算法变为插入排序,这就保证了数据一定会被排序。
* 希尔排序的关键,并非随便的分组然后各自排序,而是将相隔某个“增量”的记录组成一个子序列,实现跳跃式移动,使得排序效率提高。
* </p>
*/
public class ShellSort {
public static void main(String[] args)
{
int i = 0;
int a[] = {6, 5, 3, 1, 8, 7, 2, 4, 9, 0};
int len = a.length;
shellSort(a);
}
private static void shellSort(int arr[]){
int length = arr.length;
int i,j;
int h;
int temp;
// 确定步长h(确定步长序列以及需要几趟完成希尔排序)
for (h = length/2; h > 0; h = h/2){
// 遍历子序列,每次下标增量为h
for (i = h; i < length; i ++){
temp = arr[i]; //待插入元素
// 子序列插入比较。可能会有疑问,为什么不是按照图示的[32,26,60]来逐个比较呢?因为代码不需要像图示那样,额外花销来维护第几趟。
// 当i自增到60的下标时,下面的for循环,相当于会插入比较到[26,32]的子序列里面了。这是非常秒的!
for (j = i-h; j >= 0; j -= h){
if (temp < arr[j]){
//较大值往后挪
arr[j + h] = arr[j];
} else {
break;
}
}
// 此时的j<0,因此temp赋值的意思是,将原本较小值移动到前面
arr[j+h] = temp;
}
}
for (int a=0; a<arr.length; a++){
System.out.println("序列的第"+a+"元素是:"+arr[a]);
}
}
}
结果输出
序列的第0元素是:0
序列的第1元素是:1
序列的第2元素是:2
序列的第3元素是:3
序列的第4元素是:4
序列的第5元素是:5
序列的第6元素是:6
序列的第7元素是:7
序列的第8元素是:8
序列的第9元素是:9
排序算法 | 最好时间 | 平均时间 | 最坏时间 | 辅助存储 | 稳定性 | 备注 |
希尔排序 | Ο(n) | O(nlog(n)) | O(n^s) (1<s<2) |
Ο(1) | 不稳定 |
s是所选分组 |
1)文章开头,在确定步长序列时,挖了一个坑(没有给出具体的确定步长的方法),这里给大家做个解答。
答:其实希尔排序的步长序列,常用的增量是以2为跨度的增量,所以每一次排序的时候那么增量都为上一次的一半,这也就是我们常说的希尔增量。
2)希尔排序首先要解决的问题是使得数组内部的序列大部分是有序的,怎么样实现这个想法呢?
答:方法是将数组按照不同的间隔分成若干个序列,在序列内进行直接插入排序,最后进行一趟直接插入排序。一般来说算法的时间复杂度低于O(n ^ 2),但是在极端的情况下,希尔排序的时间复杂度仍然是O(n ^ 2),甚至比直接比直接插入排序更慢,因为数组的在划分增量的时候每一次都没有在内部移动元素进行排序,因为在划分的序列中都是有序的,等到最后一次增量为1的时候那么菜进行直接插入排序这个时候排序就比直接插入排序更慢了。
3)希尔排序是稳定排序吗?
答:否。因为步长确定以后,每个子序列内部会涉及到元素的交换,所以两个相同元素,可能在步长不一样的情况下,导致位置挪动。(由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的)
扫码二维码
获取知识干货
后台技术汇
点个“在看”表示朕
已阅