前提说明:有关插入排序的三种方式
(1)插入排序的基本方法是:每步将一个待排序的元素,按其排序码大小插入到前面已经排好序的一组元素的适当位置上去,直到元素全部插入为止。
(2)可以选择不同的方法在已经排好序的有序数据表中寻找插入位置,依据查找方法的不同,有多种插入排序方法。下面是常用的三种。
- 直接插入排序
- 折半插入排序
- 希尔排序
三种方式思想
(3)直接插入排序基本思想:当插入第i(i>1)个元素时,前面的data[0],data[1]……data[i-1]已经排好序。这时用data[i]的排序码与data[i-1],data[i-2],……的排序码顺序进行比较,找到插入位置即将data[i]插入,原来位置上的元素向后顺序移动。
(4)折半插入排序基本思想:设元素序列data[0],data[1],……data[n-1]。其中data[0],data[1],……data[i-1]是已经排好序的元素。在插入data[i]时,利用折半搜索法寻找data[i]的插入位置。
(5)希尔排序的过程相比前两种有些不同,下面我们主要介绍希尔排序的过程实现。
希尔排序算是对简单插入排序的一种改进,然而属于一种增量式的排序算法
希尔排序是把序列按一定间隔分组,对每组使用直接插入排序;随着间隔减小,一直到1,使得整个序列有
一、第一趟取increment的方法是:n/3向下取整+1=3(关于increment的取法之后会有介绍)。将整个数据列划分为间隔为3的3个子序列,然后对每一个子序列执行直接插入排序,相当于对整个序列执行了部分排序调整。图解如下:
二、第二趟将间隔increment= increment/3向下取整+1=2,将整个元素序列划分为2个间隔为2的子序列,分别进行排序。图解如下:
三、第三趟把间隔缩小为increment= increment/3向下取整+1=1,当增量为1的时候,实际上就是把整个数列作为一个子序列进行插入排序,图解如下:
到了这有一个问题摆在大家面前了,也就是说一开始这个间隔应该取多少比较好呢?这里有一个公式:
上面10条数据,所以第一个增量是5。
关于希尔排序increment(增量)的另外的一种取法:
增量increment的取法有各种方案。最初shell提出取increment=n/2向下取整,increment=increment/2向下取整,直到increment=1。但由于直到最后一步,在奇数位置的元素才会与偶数位置的元素进行比较,这样使用这个序列的效率会很低。后来Knuth提出取increment=n/3向下取整+1.还有人提出都取奇数为好,也有人提出increment互质为好。应用不同的序列会使希尔排序算法的性能有很大的差异。
二、代码实现
- 我们来看一下简单的实现:
(1)对希尔排序的时间复杂度分析很困难,在特定情况下可以准确的估算排序码的比较次数和元素移动的次数,但要想弄清楚排序码比较次数和元素移动次数与增量选择之间的依赖关系,并给出完整的数学分析,还没有人能够做到。
(2)这里我们把3种常用的插入排序做一个程序测试,通过每种算法测试所执行的时间,来定性的认识希尔排序的性能优劣。测试的思路是通过生成1000个1——10000之间的随机数,令三种排序算法分别对其进行排序,输出排序所花费的时间。
(3)测试的程序源码(Java)
分别使用三种方式(直接插入法,折半插入法,希尔插入法)
最后测试分别测试十次,所得出:
直接插入:187 / 10s;
折半插入:138 / 10;
希尔插入: 47/10;
由于时间问题,暂且测试十次;
有兴趣的朋友,可以自己测试,这里有两个思路我觉得需要考虑:
(1)对增量序列优化:使用更加复杂的方式。
(2)对每一趟的插入排序进行优化:在内循环中,总是将较大的元素向右移动。
package src;
import sun.security.util.AuthResources_it;
import java.util.ArrayList;
import java.util.Random;
/**
* @Classname shell_sort
* @Description TODO
* @Date 2020/5/26 21:45
* @Created by ziye
*/
public class shell_sort {
static ArrayList<Integer> numbers=new ArrayList<Integer>();
public static void main(String[] args) {
int time_t;
long c_start;
long c_end;
produceRandomNumbers(1, 1000, 1000);
c_start = System.currentTimeMillis();
// dinsert_sort(0, 999);
// binsert_sort(0, 999);
shell_sort(0, 999);
System.out.println("----------------------");
c_end = System.currentTimeMillis();
for (int i:numbers){
System.out.println(i);
}
System.out.println("当前排序算法花费时间为:"+(c_end-c_start) + "ms" );
}
/*
* 插入排序算法
*/
// vector<int> numbers{3, 2, 4, 6, 1, 9, 5, 8, 7, 10};
//vector<int> numbers{72, 6, 57, 88, 60, 42, 83, 73, 48, 85};
//vector<int> numbers{21, 25, 49, 25, 16, 8};
//函数功能,直接插入算法对数字排序
//函数参数,数列起点,数列终点
public static void dinsert_sort( int start, int end) {
for (int i = start + 1; i <= end; ++i) {
if (numbers.get(i) < numbers.get(i - 1)) {
int temp = numbers.get(i);
int j = i - 1;
do { //依次移动并寻找插入位置
numbers.set(j + 1, numbers.get(j));
--j;
} while (j >= start && numbers.get(j) > temp);
numbers.set(j + 1, temp); //插入元素
}
}
}
//函数功能,折半插入算法对数字排序
//函数参数,数列起点,数列终点
public static void binsert_sort( int start, int end) {
int low = 0, high = 0, middle = 0;
for (int i = start + 1; i <= end; ++i) {
int temp = numbers.get(i);
low = start;
high = i - 1;
while (low <= high) { //折半搜索寻找插入位置
middle = (low + high) / 2;
if (numbers.get(middle) > temp) {
high = middle - 1; //定位到前半部分
} else {
low = middle + 1; //定位到后半部分
}
}
for (int k = i - 1; k >= low; --k) {
numbers.set(k + 1, numbers.get(k)); //成块移动,空出插入位置
}
numbers.set(low, temp); //插入元素
}
}
//函数功能,希尔排序算法对数字递增排序
//函数参数,数列起点,数列终点
public static void shell_sort( int start, int end) {
System.out.println("-------123---------");
int increment = end - start + 1; //初始化划分增量
int i;
int j;
int temp;
//每次减小增量,直到increment = 1
while (increment>=1){
for (i = increment; i <= end; i++) { //对每个划分进行直接插入排序
temp = numbers.get(i);
j = i - increment;
while (j>=0&& numbers.get(j) <temp){ //移动元素并寻找位置
numbers.set(j + increment, numbers.get(j));
j -= increment;
}
numbers.set(j + increment, temp); //插入元素
}
increment = increment / 2;
}
}
//函数功能,随机产生amount个start——end内的随机数并存入指定容器
//函数参数,随机数范围起点,随机数范围终点,随机数生成数量
public static void produceRandomNumbers( int start, int end, int amount) {
for (int cnt = 1; cnt <= amount; cnt++) {
Random rand = new Random();
int q=rand.nextInt(end) + start;
// System.out.println(q);
numbers.add(q);
}
}
}
参考原文链接:https://blog.csdn.net/weixin_37818081/article/details/79202115
参考原文链接:https://baijiahao.baidu.com/s?id=1645338224617537073&wfr=spider&for=pc