java 实现 常见排序算法(二) 插入排序

4 篇文章 0 订阅

大家好,我是烤鸭:    

   今天分享一下基础排序算法之直接插入排序。

 

1.     直接插入排序:

原理:假设前面的数为有序数列,然后有序数列与无序数列的每个数比较,我们可以从右向左比较

思路:从第2个数开始,和1比较。这样前2个有序。

           第3个和前2个比较,这样前3个有序。(如果是最小的,则第3个元素处在第1个位置,后面的元素后移1。)

           第4个和前3个比较,同上。

           直到第 n 个元素 和 前 n-1 个比较。

代码实现:

    /**
     * 直接插入排序
     * directInsertSort
     *
     * @param array 时间复杂度,O的n^2
     * 直接插入排序就是我们假设前面的数为有序数列,然后有序数列与无序数列的每个数比较,我们可以从右向左比较
     * 当 array[i]<=array[j]=
     */
    public void directInsertSort(int[] array) {
        long nowTime = System.currentTimeMillis();
        int tem = 0;
        for (int i = 1; i < array.length; i++) {
            int j = i - 1;
            tem = array[i];
            for (; j >= 0 && array[j] > tem; j--) {
                array[j + 1] = array[j];//将大于array[i]的数整体后移一单位
            }
            array[j + 1] = tem;
        }
        System.out.println("直接插入排序,花费时间(s):" + ((System.currentTimeMillis() - nowTime) / 1000.0) + "s");
    }

2.     折半插入排序(优化):

思路:

其实和直接插入排序是类似的,只是在遍历元素的时候采用的是二分法,直插采用的是顺序遍历。

取 temp 作为当前元素,

begin从0开始,end到数组最后一个元素。

如果temp < 中间值,begin从中间值+1继续,否则 end 变为 end - 1 继续,

begin 到 i 整体后移。

如图:(图片来源 http://www.cnblogs.com/chengxiao/p/6103002.html)

代码实现:

/**
     * 折半插入排序
     * @param source
     * halfInsertSort
     *
     * @param source 时间复杂度,O的n^2
     * 折半插入排序算法是一种稳定的排序算法,比直接插入算法明显减少了关键字之间比较的次数,
     * 因此速度比直接插入排序算法快,但记录移动的次数没有变,所以折半插入排序算法的时间复杂度仍然为O(n^2),
     * 与直接插入排序算法相同
     */
    public static void halfInsertSort(int[] source) {
        long nowTime = System.currentTimeMillis();
        int size = source.length;
        for (int i = 1; i < size; i++) {
            // 拿出来
            int temp = source[i];
            int begin = 0; // 标记排好序的数组的头部
            int end = i - 1; // 标记排好序数组的尾部
            // 只要头部一直小于尾部,说明temp还在2个标记范围内
            while (begin <= end) {
                // 取2个标记的中间数据的值
                int mid = (begin + end) / 2;
                // 比较,若比中间值大,则范围缩小一半
                if (temp > source[mid]) {
                    begin = mid + 1;
                    // 否则,范围也是缩小一半
                } else {
                    end = mid - 1;
                }
                // 循环结束时,end<begin,即i应该插入到begin所在的索引
            }
            // 从begin到i,集体后移
            for (int j = i; j > begin; j--) {
                source[j] = source[j - 1];
            }
            // 插入i
            source[begin] = temp;
        }
        System.out.println("折半插入排序,花费时间(s):" + ((System.currentTimeMillis() - nowTime) / 1000.0) + "s");
    }

3.     shell排序

思路:

Shell排序也是对直接插入排序的改进。它实质上是一种分组插入方法。

下面希尔排序的步长选择都是从n/2开始,每次再减半,直到最后为1。

时间复杂度 : O(nlog2^n)

 /**
     * 希尔排序
     * 针对直接插入排序的下效率问题,有人对次进行了改进与升级,这就是现在的希尔排序。
     * 希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。
     * 首先确定分的组数。
     * 然后对组中元素进行插入排序。
     * 然后将length/2,重复1,2步,直到length=0为止。
     * @param arr
     */
    public void shellSort(int [] arr){
        long nowTime = System.currentTimeMillis();
        int len=arr.length;//单独把数组长度拿出来,提高效率
        while(len!=0){
            len=len/2;
            for(int i=0;i<len;i++){//分组
                for(int j=i+len;j<arr.length;j+=len){//元素从第二个开始
                    int k=j-len;//k为有序序列最后一位的位数
                    int temp=arr[j];//要插入的元素
                    /*for(;k>=0&&temp<arr[k];k-=len){
                        arr[k+len]=arr[k];
                    }*/
                    while(k>=0&&temp<arr[k]){//从后往前遍历
                        arr[k+len]=arr[k];
                        k-=len;//向后移动len位
                    }
                    arr[k+len]=temp;
                }
            }
        }
        System.out.println("希尔排序,花费时间(s):" + ((System.currentTimeMillis() - nowTime) / 1000.0) + "s");
    }

 

耗时对比:

10W 条随机 数据 运行如图:

可以看出希尔排序时间明显(比直插排序和折半排序)缩短。折半排序和直插排序时间差不多。

50W 条随机 数据 运行如图:

可以看出希尔排序时间明显(比直插排序和折半排序)缩短。折半排序和直插排序时间差不多。

100W 条随机 数据 运行如图:

可以看出希尔排序时间明显(比直插排序和折半排序)缩短。折半排序比直插排序耗时更多。

总结:

直接插入排序写法比较简单,平均时间复杂度为:O(n^2) 。

折半插入排序,平均时间复杂度为:O(n^2) 。

希尔排序,平均时间复杂度为:O(nlog2^n) 。

各种排序方法比较:

 

更多排序算法:

冒泡排序   :  https://blog.csdn.net/Angry_Mills/article/details/81057900

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烤鸭的世界我们不懂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值