希尔排序
这里开始介绍第一种复杂排序算法——希尔排序。
我们知道插入排序在有些时候即数据本身基本有序(较小的数据基本在前面,较大的数据基本在后面)或者数据数目较少的时候,由于此时数据数目较少且基本有序,可以使得插入排序这种邻位移动的次数不会太多,所以此时效率较高。希尔排序就是基于此事实,对插入排序进行改进,简单来说,希尔排序就是高效版的插入排序,其核心思想是根据一个步长,将序列分成多个子序列(子序列的数目就是步长值,但子序列的长度不定,但与步长成反比),每个子序列中的元素都相隔一个步长,这样每个子序列的长度相比原数组序列短,然后分别对这些子序列进行插入排序,这样较小的元素尽可能地排在了前面,较大的元素尽可能地排在了后面,这样就使得原序列基本有序,完成上述步骤后就完成了一轮希尔排序,接下来缩短步长重复上述操作,由于经过一轮希尔排序后数组变的基本有序,所以下一轮希尔排序效率就会提高,直至步长为1,最终完成排序。可见一轮希尔排序就是对多个子序列的插入排序,而我们需要多轮希尔排序才能完成最终的排序,而且最后一轮希尔排序其步长必须为1,这样才能保证数组序列真正有序。
下面解析希尔排序的代码,相比原两层循环的插入排序,这里是三层嵌套for循环,最外一层是对每个子序列进行遍历,里面两层就是是对所给定的子序列进行插入排序
public static void xiEr(int[] arr,int dk){//这里得给出步长值
for(int i=0;i<dk;i++){
for(int j=1;i+j*dk<arr.length;j++){//这里i+j*dk才是数组中的相应子序列元素的角标,j则是控制相应子序列中的元素位置
for(int k=i+j*dk;k>i && arr[k]<arr[k-dk];k-=dk){//这里就是仿照插入排序的思路,注意k>i而不是k>0,另外k得自减dk
int temp=arr[k];
arr[k]=arr[k-dk];
arr[k-dk]=temp;
}
}
}
}
我们可以发现希尔排序中子序列中元素的交换是跳跃式的移动,从而比传统的插入排序那种邻位移动的移动距离更大,所以效率更高。但也是因为跳跃式移动,导致它并不是稳定的排序算法。这里希尔排序的步长选择非常关键,由于涉及到数学问题,目前没有最佳方案。平时使用希尔排序步长可以依次选择5、3和1,这里注意当步长为1时,就退化成普通的插入排序了,所以说如果第一轮希尔排序就选择步长为1,那么这与普通插入排序没有区别,所以需要进行多个步长的希尔排序才能完成最终的排序。目前发现希尔排序的最优时间复杂度可以达到O(n^1.3),但最差的时间复杂度为O(n^2),其平均时间复杂度没有定值,约为O(nlogn)~O(n^2),其空间复杂度为O(1)。最后需要明确的是在记录数较少的时候,希尔排序并没有体现出优势,当记录数较多的情况下,此时希尔排序这种跳跃式移动的优势就显现出来了,人家能够更加直接简单快速地移动元素,当然复杂排序算法就是应用在这种数据数目较多的场景中。
相关阅读
最开始学习编程,遇到排序问题,一般都是用冒泡法,因为冒泡法好理解,代码量少。但是这种算法时间复杂度高,当需要排序的元素较多时,程序运
自顶向下的归并排序(Merge Sort) O(nlogn)
归并排序的基本思想: 该算法是采用分治法来实现的,将原来的数组不断对半分,直到分得每个数组含有一个元素后,再一层一层归并的过
##原理: 快速排序,说白了就是给基准数据找其正确索引位置的过程. 如下图所示,假设最开始的基准数据为数组第一个元素23,
Mysql| order by 排序检索数据(ASC,DESC)
在myslq数据中,检索出来的数据往往是以底层数据添加到表中的顺序显示的,但是可能存在更新和删除操作,这样就会影响排序顺序,所有
冒泡排序分从大到小和从小到大两种排序方式。它们的唯一区别就是两个数交换的条件不同,从大到小排序是前面的数比后面的小的时候交