10大排序算法之三:插入排序【稳定的】,复杂度高,系统常在数据少时用插入排序。

提示:整个算法界,一共有十大排序算法,每一个算法都要熟悉,才算是算法入门

算法界的十大排序算法分别是:
选择排序、冒泡排序、插入排序、堆排序、希尔排序、归并排序、快速排序、桶排序、计数排序,基数排序
选择排序:10大排序算法之一:选择排序【不稳定】,一般不用选择排序的
冒泡排序:10大排序算法之二:冒泡排序【稳定的】,但复杂度高,一般不用冒泡排序的
在这里插入图片描述
如果你想进互联网大厂,至少这上面的其中最重要的8种,是必须熟练掌握的:
去掉希尔排序,希尔排序是一种改进的插入排序算法——所以只需要掌握插入排序
桶排序中最重要的就是计数排序和基数排序,都是桶的思想
在这里插入图片描述
根据算法复杂度低一点的,又稳定的
咱们可以最常用的算法实际上就四种:
插入排序(o(n^2))【当数据量小时,这个方法简单】【稳定】、
堆排序o(nlog(n))【不稳定】、
归并排序o(nlog(n))【稳定】,
快速排序o(nlog(n))(虽然快排不稳定,但是很多不需要稳定情况下,快排非常快)
因此,o(n)的桶排序很少用,除非面试官特别申明,否则都用比较排序。
归并排序是最常用的,复杂度低,而且稳定,达到了一个非常好的折中。


题目

请你手撕插入排序的算法代码,要求将arr中的数字升序排序。


一、审题

示例:arr = 5 3 1 8 6 2 4
让其最终变为:arr= 1 2 3 4 5 6 8


二、解题

插入排序是稳定的排序,但复杂度高o(n^2),系统中一般在数据量很少时,使用插入排序算法

插入排序的核心思想:
外部从i=1–N-1宏观调度
内部,从j=i–1向左遍历,对比此时j和j-1位置的数,若j更小,交换j与j-1位置
这样子跟冒泡排序相反的书序冒泡,冒泡是冒大数到右边,插入式冒小的到左边,相当于让小的数往左沉入

来看个例子就知道了:
arr = 5 3 1 8 6 2 4
(1)最开始i=1,j从1<–i=1开始对比,53交换,固定了35;
(2)然后i=2,j从1<–i=2开始对比,15交换,13交换,固定了135;
(3)然后i=3,j从1<–i=3开始对比,8不比谁小,谁也不交换,固定了1358;
(4)然后i=4,j从1<–i=4开始对比,86交换,然后停止,固定了13568;
(5)然后i=5,j从1<–i=5开始对比,82交换,62交换,52交换,42交换,32交换,然后停止,固定了123568;
(6)然后i=6,j从1<–i=6开始对比,84交换,64交换,54交换,然后停止,固定了1234568;
排序完成!
在这里插入图片描述
发现了啥没??跟冒泡排序它不太一样的地方时,虽然算法的复杂度为o(n^2)
但是,冒泡排序,它竟然对比了很多的地方,但是压根不交换
二这里,插入排序,一旦对比,一次不交换,今后都不交换了,
因此插入排序,大大减少了对比次数,整体来说,比冒泡排序好多了
故,系统在数据量少的时候,经常用插入排序,看不出来谁快谁慢,但是这个代码简单。

手撕代码:

public static void insertSortReview(int[] arr){
        int  N = arr.length;
        //外部宏观调度室i从1--N-1,考虑到时j与j-1位置
        for (int i = 1; i < N; i++) {
            //里面时j从i--1调度,需要与前面j-1比较
            for (int j = i; j > 0; j--) {
                //但凡遇到哪个j小与j-1的数,交换两两位置
                if (arr[j] < arr[j - 1]) swap(arr, j - 1, j);//将小数冒到左边去
            }
        }
    }

    //常用的交换数组函数
    public static void swap(int[] arr, int i, int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

对数器校验:
不知道啥叫对数器的,先把前面2个文章看了【文章开头那说的10大算法之一和二】

//对数器之构建随机数组
    public static int[] createArray(int arrSize, int maxValue){
        int[] arr = new int[arrSize];
        for (int i = 0; i < arrSize; i++) {
            arr[i] = (int)(maxValue * Math.random());//0-N-1的随机数
        }
        return arr;
    }

    public static void checker(){
        //生成检验数组
        int[] arr = createArray(10000,10000);
        int[] arr2 = new int[arr.length];//赋值同样一个数组arr
        for (int i = 0; i < arr.length; i++) {
            arr2[i] = arr[i];//copy即可
        }
        int[] arr3 = new int[arr.length];//赋值同样一个数组arr
        for (int i = 0; i < arr.length; i++) {
            arr3[i] = arr[i];//copy即可
        }

        //绝对的正确方法——暴力方法,或系统函数,操作arr
        Arrays.sort(arr);
        //优化方法,操作arr2
        insertSort(arr2);
        insertSortReview(arr3);

        //然后两个数组对位校验
        boolean isSame = true;
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] != arr2[i]) {
                isSame = false;
                break;//只要有一个不等,不必继续判断了
            }
        }
        System.out.println(isSame == false ? "oops,wrong!" : "right!");
        isSame = true;
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] != arr3[i]) {
                isSame = false;
                break;//只要有一个不等,不必继续判断了
            }
        }
        System.out.println(isSame == false ? "oops,wrong!" : "right!");
    }

测试:

public static void main(String[] args){
        checker();
    }

结果美滋滋:

right!
right!

另外呢,这个插入排序算法,有一个改进版本的算法,叫希尔排序
它面试的时候不考希尔排序
但是大致了解一下吧,希尔排序就是每次以gap=4的区间,玩插入排序
啥意思呢,下图看,每次相当于只玩4这么短的插入排序,所以相对来说
间隔很大的,移动的次数变少了
间隔小的,移动的距离变短了
节约很多的对比和交换的次数,速度变快了
复杂度比插入排序好点点,o(n^(1.3))
但是,由于设计了gap,就导致排序它不稳定了!!可能相同的数,在gap之间会换位置,这就没得玩了。

这个不管,不考的!
在这里插入图片描述
这也是为啥我说10大排序算法只需要记8个的原因!!!
选择排序、冒泡排序、插入排序、堆排序希尔排序归并排序、快速排序桶排序计数排序,基数排序


总结

提示:重要经验:

1)插入排序,大大减少了对比次数,整体来说,比冒泡排序好多了,故,系统在数据量少的时候,经常用插入排序,看不出来谁快谁慢,但是这个代码简单。
2)

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐