希尔排序算法从简单到困难的推导过程
算法思路
希尔排序算法是于1959年提出的一种排序算法,也称作是一种插入排序算法,是简单的插入排序算法的一种改进,也成为缩小增量排序
基本思路:希尔排序是把记录按下标的一定增量分组,对每一个组内进行插入排序算法,随着增量(由4组变成2组最后在变成1组)逐渐减小,每一组中包含的关键词越来越多,当增量减至1时,整个文件就被分成一组,在进行排序之后,算法就被终止。
1利用交换法实现希尔排序
希尔排序使用交换法的粗暴的代码实现
package com.njupt.Algorithm.xier;
import java.util.Arrays;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/07/19/10:18
* @Description:
*/
public class XiEr {
public static void main(String[] args) {
int[] arr = {7, 6, 5, 4, 3, 2, 1, 0};
//第一步:先分成4组,故每组中有两个元素如果没有越界的话,arr[j]和arr[j+4]为一组,故分成了两组。
int temp = 0;
for (int i = 4; i < arr.length; i++) {
for (int j = i - 4; j >= 0; j -= 4) {
//如果每一组中前一个数比后一个数大,则需要交换
if (arr[j] > arr[j + 4]) {
temp = arr[j + 4];
arr[j + 4] = arr[j];
arr[j] = temp;
}
}
}
System.out.println("第一次分组排序:");
System.out.println(Arrays.toString(arr));
//第二次分组,分成2组,每组里面4个元素,然后进行交换比较
for (int i = 2; i < arr.length; i++) {
for (int j = i - 2; j >= 0; j -= 2) {
//如果每一组中前一个数比后一个数大,则需要交换
if (arr[j] > arr[j + 2]) {
temp = arr[j + 2];
arr[j + 2] = arr[j];
arr[j] = temp;
}
}
}
System.out.println("第2次分组排序:");
System.out.println(Arrays.toString(arr));
//第三次只剩下一组了,对所有元素进行排序之后:
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1; j >= 0; j -= 1) {
//如果每一组中前一个数比后一个数大,则需要交换
if (arr[j] > arr[j + 1]) {
temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
}
System.out.println("第3次分组排序:");
System.out.println(Arrays.toString(arr));
}
}
结果:
第一次分组排序:
[3, 2, 1, 0, 7, 6, 5, 4]
第2次分组排序:
[1, 0, 3, 2, 5, 4, 7, 6]
第3次分组排序:
[0, 1, 2, 3, 4, 5, 6, 7]
Process finished with exit code 0
和预期的一致。
将上述的代码进行总结,发现规律之后,就得出下面这个交换法的希尔排序算法。
用交换排序法的希尔排序算法
package com.njupt.Algorithm.xier;
import java.util.Arrays;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/07/19/11:31
* @Description:
*/
public class RealXiEr {
public static void main(String[] args) {
int[] arr = {7, 6, 5, 4, 3, 2, 1, 0};
//第一步:先分成gap组,故每组中有两个元素如果没有越界的话,arr[j]和arr[j+gap]为一组
int temp = 0;
int count = 0;
for (int gap = arr.length / 2; gap >= 1; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
for (int j = i - gap; j >= 0; j -= gap) {
//如果每一组中前一个数比后一个数大,则需要交换
if (arr[j] > arr[j + gap]) {
temp = arr[j + gap];
arr[j + gap] = arr[j];
arr[j] = temp;
}
}
}
System.out.println("第" + (++count) + "次分组排序:");
System.out.println(Arrays.toString(arr));
}
}
}
当数据多的时候,这个交换法实现希尔排序算法,因为需要交换的次数比较多,故对此进行改进,改变原来的交换算法,变成插入排序法。
2利用插入排序法的希尔排序算法
首先要了解什么叫做插入排序算法
具体的插入算法可以参考小悦悦的未婚夫写的这一篇博客,简单说一下基本思路
基本思路就是:相当于创建一个新的空数组,为有序数组。(其实还是在无序数组中进行排序的)
- 第一步先把无序数组中的第一个数放到有序数组中
- 从无序数组中取出第二个数,通过和有序数组中的数进行比较
- 确定存放的位置,并存放
- 从无序数组中取出第3个数,然后重复步骤2,直至无序数组中的数全部取完,则算法结束。
注意:插入排序算法中,从无序数组中取出的数总是假设放到有序数组中的最后一个位置
回忆插入算法
//首先回忆一下插入法,插入法就是找到新的数组来放那个数,并且比较找到插入的位置,每次都是先假设查到最后面
int[] arr={7,6,5,4,3,2,1,0};
//因为下标为0的数已经放到数组中了,故假设从无序数组中的第二个数开始比较
for(int i=1;i<arr.length;i++){
//先确定要插入的数
int insertVal=arr[i];
//假设插入到有序数组中的最后一个位置
int insertIndex=i-1;
//如果要插入的数比插入下标位置处的小,就让原始下标的位置移到下一位
while(insertIndex>=0&&insertVal<arr[insertIndex]){
//
arr[insertIndex+1]=arr[insertIndex];
//接着往前进行比较
insertIndex--;
}
//当不满足以上条件,就是找到了最小值的位置,插入即可
arr[insertIndex+1]=insertVal;
System.out.println(Arrays.toString(arr));
}
System.out.println(Arrays.toString(arr));
但是原始的插入算法存在的问题是当数组中最小的数放到了最后之后,需要查找的次数比较多,故引入了希尔排序算法,来对排序前先进行微调数据,然后在进行插入排序。
用最粗暴原始的方式利用插入排序来实现希尔排序
根据希尔排序算法有:
第一步分成4组,故每一组中有2个元素,让这两个数进行插入排序
//分成4组,将每一个小组中的两个元素,进行插入排序
//第一组
int insertVal=arr[4];
int insertIndex=0;
while(insertIndex>=0&&insertVal<arr[insertIndex]){
arr[insertIndex+4]=arr[0];
insertIndex-=4;
}
arr[insertIndex+4]=insertVal;
//第二组
insertVal=arr[5];
insertIndex=1;
while(insertIndex>=0&&insertVal<arr[insertIndex]){
arr[insertIndex+4]=arr[0];
insertIndex-=4;
}
arr[insertIndex+4]=insertVal;
//第三组
int insertVal=arr[6];
int insertIndex=2;
while(insertIndex>=0&&insertVal<arr[insertIndex]){
arr[insertIndex+4]=arr[0];
insertIndex-=4;
}
arr[insertIndex+4]=insertVal;
//第四组
insertVal=arr[7];
insertIndex=3;
while(insertIndex>=0&&insertVal<arr[insertIndex]){
arr[insertIndex+4]=arr[0];
insertIndex-=4;
}
arr[insertIndex+4]=insertVal;
可以看出:每组数中相差元素的下标为4
总结第一步代码
for(int j=4;j<arr.length;j++){
int insertVal=arr[j];
int insertIndex=j-4;
while(insertIndex>=0&&insertVal<arr[insertIndex]){
arr[insertIndex+4]=arr[insertIndex];
insertIndex-=4;
}
arr[insertIndex+4]=insertVal;
}
System.out.println(Arrays.toString(arr));
第二步分成两组,每组中有四个元素,让这四个数进行插入排序
简单写几个,用来找到规律
//第一组中先对前两个数进行排序
int insertVal=arr[2];
int insertIndex=0;
while(insertIndex>=0&&insertVal<arr[insertIndex]){
arr[insertIndex+2]=arr[insertIndex];
insertIndex-=2;
}
arr[insertIndex+2]=insertVal;
//第二组中对前两个数进行排序
insertVal=arr[3];
insertIndex=1;
while(insertIndex>=0&&insertVal<arr[insertIndex]){
arr[insertIndex+2]=arr[insertIndex];
insertIndex-=2;
}
arr[insertIndex+2]=insertVal;
//第一组中,对前三个数进行排序
insertVal=arr[4];
insertIndex=2;
while(insertIndex>=0&&insertVal<arr[insertIndex]){
arr[insertIndex+2]=arr[insertIndex];
insertIndex-=2;
}
arr[insertIndex+2]=insertVal;
//以此类推
可以看出:每组数中相差元素的下标为2
总结第二步代码
//第二次排序分成2组,每组有4个元素
for(int j=2;j<arr.length;j++){
int insertVal=arr[j];
int insertIndex=j-2;
while(insertIndex>=0&&insertVal<arr[insertIndex]){
arr[insertIndex+2]=arr[insertIndex];
insertIndex-=2;
}
arr[insertIndex+2]=insertVal;
}
System.out.println(Arrays.toString(arr));
最后只能划分成一组,直接进行插入排序算法即可
总结第三步代码:
for(int j=1;j<arr.length;j++){
int insertValue=arr[j];
int insertIndex=j-1;
while(insertIndex>=0&&insertValue<arr[insertIndex]){
arr[insertIndex+1]=arr[insertIndex];
insertIndex--;
}
arr[insertIndex+1]=insertValue;
}
System.out.println(Arrays.toString(arr));
然后总结一下插入排序算法实现希尔排序的规律
- 首先最明显的就是每次分组的数目gap不同4–>2–>1,也就是数组长度/2
- 然后每次组内查找时,都是根据这个gap来进行组内的调整步长来进行比较插入排序
有了以上规律,就可以实现用插入法实现希尔排序算法了
真正的用插入排序算法实现希尔排序算法
将以上三个总结的代码再次总结之后,就可以实现
int[] arr={7,6,5,4,3,2,1,0};
//开始希尔排序:思路分析是对其分组,然后在插入
//每次的分组为gap个
for(int gap=arr.length/2;gap>=1;gap=gap/2){
for(int j=gap;j<arr.length;j++){
int insertVal=arr[j];
int insertIndex=j-gap;
while(insertIndex>=0&&insertVal<arr[insertIndex]){
arr[insertIndex+gap]=arr[insertIndex];
insertIndex-=gap;
}
arr[insertIndex+gap]=insertVal;
}
System.out.println(Arrays.toString(arr));
}
结果
[3, 2, 1, 0, 7, 6, 5, 4]
[1, 0, 3, 2, 5, 4, 7, 6]
[0, 1, 2, 3, 4, 5, 6, 7]
Process finished with exit code 0
心得体会
上来如果直接给出希尔算法的代码肯定是都是很难理解的,但是拆开一步一步走之后发现其实算法并不难,只要先从简单的步骤开始走起,当找到规律之后,在进行总结归纳,就可以了解算法的真正原理和使用了!!