插入排序-希尔排序(缩小增量排序)

本文详细介绍了希尔排序的基本思想、动画演示及两种实现方式:交换法和位移法。通过Java代码展示了这两种方法,并通过时间测试表明位移法在效率上优于交换法。希尔排序是一种改进的插入排序,通过分组和增量逐步缩小排序的范围,最终达到整体有序。文章还探讨了其时间复杂度和空间复杂度。
摘要由CSDN通过智能技术生成

返回目录

出现的必要性

简单的插入排序可能存在的问题.
数组 arr = {2,3,4,5,6,1} 这时需要插入的数 1(最小), 这样的过程是:
{2,3,4,5,6,6}
{2,3,4,5,5,6}
{2,3,4,4,5,6}
{2,3,3,4,5,6}
{2,2,3,4,5,6}
{1,2,3,4,5,6}
结论: 当需要插入的数是较小的数时,后移的次数明显增多,对效率有影响。
——尚硅谷

基本思想

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含
的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。
希尔排序有两种方式:交换法,移动法。
——尚硅谷

简单来说:分而治之+插入排序

动画演示

在这里插入图片描述

希尔排序示例

希尔排序-交换法

Java代码:

package sort;

import java.util.Arrays;
import java.util.Scanner;

public class ShellSort {
    public static void main(String[] args) {
        int[] arr = inputArray();
        System.out.printf("输入的数组为:%s\n",Arrays.toString(arr));
        shellSortExchange(arr);
    }
    
    /**
     * 输入数组
     * @return
     */
    public static int[] inputArray(){
        int s;
        System.out.printf("请输入数组的元素个数:");
        Scanner scanner = new Scanner(System.in);
        s = scanner.nextInt();
        int[] arr = new int[s];
        System.out.println("请输入数组的数值,以回车结束一个值的输入:");
        for (int i = 0; i < s; i++) {
            arr[i] = scanner.nextInt();
        }
        return arr;
    }

    /**
     * 希尔排序-交换法
     * @param arr
     */
    public static void shellSortExchange(int[] arr){
        int temp,count=0;
        for (int page = arr.length/2; page > 0; page/=2) {//确定增量
            count++;
            for (int i = page; i < arr.length; i++) {//确定每一轮的循环次数
                for (int j = i-page; j >= 0; j-=page) {//确定要进行对比的组中的每一个元素
                    if(arr[j] > arr[j+page]){
                        temp = arr[j];
                        arr[j] = arr[j+page];
                        arr[j+page] = temp;
                    }
                }
                System.out.printf("第%d轮第%d次的排序:%s\n",count,i-page+1,Arrays.toString(arr));
            }
            System.out.printf("第%d轮排序完成后:%s\n\n",count,Arrays.toString(arr));
        }
    }
}
/* 输入的测试数据
8
9
1
7
2
3
5
4
6
0
 */

希尔排序-位移法

java代码:

package sort;

import java.util.Arrays;
import java.util.Scanner;

public class ShellSort {
    public static void main(String[] args) {
        int[] arr = inputArray();
        System.out.printf("输入的数组为:%s\n",Arrays.toString(arr));
        shellSortMove(arr);
    }

    /**
     * 输入数组
     * @return
     */
    public static int[] inputArray(){
        int s;
        System.out.printf("请输入数组的元素个数:");
        Scanner scanner = new Scanner(System.in);
        s = scanner.nextInt();
        int[] arr = new int[s];
        System.out.println("请输入数组的数值,以回车结束一个值的输入:");
        for (int i = 0; i < s; i++) {
            arr[i] = scanner.nextInt();
        }
        return arr;
    }
    
    /**
     * 希尔排序-位移法
     * @param arr
     */
    public static void shellSortMove(int[] arr){
        int count=0;
        for (int page = arr.length/2; page > 0; page/=2) {
            count++;
            for (int i = page; i < arr.length; i++) {
                int tempIndex = i;
                int temp = arr[tempIndex];
                while (tempIndex-page>=0 && temp < arr[tempIndex-page]){
                    arr[tempIndex] = arr[tempIndex-page];
                    tempIndex-=page;
                }
                arr[tempIndex]=temp;
            }
            System.out.printf("第%d轮排序完成后:%s\n",count,Arrays.toString(arr));
        }
    }
}
/* 输入的测试数据
8
9
1
7
2
3
5
4
6
0
 */

代码分析

 /**
 * 希尔排序-位移法
 * @param arr
 */
public static void shellSortMove(int[] arr){
    int count=0;
    for (int page = arr.length/2; page > 0; page/=2) {
        count++;
        for (int i = page; i < arr.length; i++) {
            int tempIndex = i;
            int temp = arr[tempIndex];
            while (tempIndex-page>=0 && temp < arr[tempIndex-page]){
                arr[tempIndex] = arr[tempIndex-page];
                tempIndex-=page;
            }
            arr[tempIndex]=temp;
        }
        System.out.printf("第%d轮排序完成后:%s\n",count,Arrays.toString(arr));
    }
}

两种希尔的思想是一样的,但是第二种方式会更能体现出插入排序的思想,第一种是每比较一个元素若是逆序状态就会进行交换,而第二种是沿用了插入排序的思想,直到直到合适的位置才进行交换。
在第一层循环中,用于确定每一次增量,就是说多少位多少位一组,以数组长度除2的方式确定每一次的增量。
第三层循环中确定每一个增量需要进行的循环次数。
第三层循:每增加一个元素都会对一整组的数据进行插入排序。例如有一个数组有100个元素,此时的增量为2,那么希尔排序会将这100个元素分为2组,那么1,3,5,7,9,11…99会处于一组,然后在1与3中要进行一次插入排序,在1,3,5中也要进行一次插入排序(因为新增进来的元素可能会对于整个队列种产生影响,所以整组的元素都要进行比较)
时间复杂度:O(n3)
空间复杂度:O(1)
稳定性:不稳定
两个值虽然会相同,但是在构造组的时,本来处于后面位置的元素可能跑到前面去。

时间测试

package sort;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;

public class ShellSort {
    public static void main(String[] args) {
        int num;//数组个数
        int[] arr, arr2;
        Scanner scanner = new Scanner(System.in);
        System.out.printf("请输入要随机生成数组的个数:");
        num = scanner.nextInt();
        arr = randomArrays(num);
        arr2 = Arrays.copyOf(arr,arr.length);
        System.out.printf("希尔插入排序前的时间:");
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        System.out.println(now.format(format));
        shellSortExchange(arr);
        System.out.printf("希尔插入排序后的时间:");
        now = LocalDateTime.now();
        System.out.println(now.format(format));
        System.out.printf("\n希尔移动排序前的时间:");
        now = LocalDateTime.now();
        System.out.println(now.format(format));
        shellSortMove(arr2);
        System.out.printf("希尔移动排序后的时间:");
        now = LocalDateTime.now();
        System.out.println(now.format(format));

    }

    /**
     * 随机生成数组函数
     * @param num 数组个数
     * @return
     */
    public static int[] randomArrays(int num){
        Random rd = new Random();
        int[] arr = rd.ints(num).toArray();
        return arr;
    }

    /**
     * 希尔排序-交换法
     * @param arr
     */
    public static void shellSortExchange(int[] arr){
        int temp,count=0;
        for (int page = arr.length/2; page > 0; page/=2) {//确定增量
            count++;
            for (int i = page; i < arr.length; i++) {//确定每一轮的循环次数
                for (int j = i-page; j >= 0; j-=page) {//确定要进行对比的组中的每一个元素
                    if(arr[j] > arr[j+page]){
                        temp = arr[j];
                        arr[j] = arr[j+page];
                        arr[j+page] = temp;
                    }
                }
            }
        }
    }

    /**
     * 希尔排序-位移法
     * @param arr
     */
    public static void shellSortMove(int[] arr){
        int count=0;
        for (int page = arr.length/2; page > 0; page/=2) {
            count++;
            for (int i = page; i < arr.length; i++) {
                int tempIndex = i;
                int temp = arr[tempIndex];
                while (tempIndex-page>=0 && temp < arr[tempIndex-page]){
                    arr[tempIndex] = arr[tempIndex-page];
                    tempIndex-=page;
                }
                arr[tempIndex]=temp;
            }
        }
    }
}

在这里插入图片描述
从排序时间前后的,时间上能够看出移动的速度明显能够比插入要快很多,数据越多差距越明显。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值