插入排序

概述

  最近在学习算法,面试时候问了我快速排序,当场懵逼了,竟然没有听说过,我支支吾吾了半天然后告诉面试官,我TM没听说过,然后面试官用一种看智障的眼神结束了面试(以上纯属虚构,哈哈),本来以为排序算法也就什么冒泡排序,快速排序什么的,今天自己一看,我姹,竟然那么多种排序算法,没办法了,只能一个一个的看咯。

核心思想

  其实核心思想很简单,就是把一个数组中的数据分成两个部分,第一部分是排好序的,第二部分是还没有排序的,然后遍历第二部分的数据,查找这个元素应该在第一个排好序的部分的什么位置,然后插入这个地方,之后把第一部分插入元素之后的部分向后移动。说着一大段,有些麻烦,下面通过一个例子讲解一下。

举例: 3,4,1,5,2,6

要求:使用插入排序算法进行排序(从小到大)

实现过程:

第一步:假设排好序的部分就是3,未排序部分为4,1,5,  2,  6

第二步:遍历第二步分数据和第一部分数据(由于现在只有一个,直接比较了),发现4>3,继续下一个,发现3>1,那1应该在3的前面,把1插入到3的位置,之后元素3和4向后移动,注意:不要动5,2,6的位置

第三步:经过第二步处理之后,排好序的部分为1,3,4 未排序的部分为5, 2,6,重复第二步

总结:简单来说,就是把未排序的数据元素寻找在排好序的数据元素中的位置,然后插入进入,之后排好序的数据元素向后移动(注意:这里是从插入的位置向后移动,并不是全部排好序的部分)

改进版本

  在上面的过程中,排好序的数组也需要一个一个遍历,最终找到元素需要插入的位置,这样时间复杂度太高,可以使用二分法查找,因为这一部分数组本身是有序的,使用二分法查找速度快很多。

举例:1,5,6,8,10,9

假设标绿的部分是已经排好序的,如果按照上面遍历的方法,需要查5次才知道9应该插入8和10之间,如果使用二分法查找,如下:

第一步:计算中间值 (0 + 4)/ 2 = 2,发现 数组中下标为2的位置元素为 6 ,而 6 < 9,继续

第二步:计算中间值 (2 + 4)/ 2 = 3,发现 数组中下标为3的位置元素为8, 而 8 < 9,继续

第三步:计算中间值 (3 + 4)/ 2 = 3.5,把3.5取整为4,之后发现下标为4的元素为10, 而 10 > 9,ok,发现9应该插入10的位置,而10应该向后移动

整个过程只需要查找3次,所以快非常多。

其实,遍历的时间复杂度为O(n),而二分法查找时间复杂度为O(log(n))

代码实现

package sort;

/**
 * @author: steve
 * @date: 2020/7/6 16:44
 * @description: 插入排序
 */
public class InsertSort {

    public static int array[] = {72, 6, 57, 88, 60, 42, 83, 73, 48, 8, 1};

    /***
     *@Description 插入排序
     *@Param []
     *@Return void
     *@Author steve
     */
    public static void insertSort() {
        //排好序的位置
        int flag = 1;
        int temp = 0;
        //外层循环控制未排序的部分
        for (int j = 1; j < array.length; j++) {
            //内层循环处理排过序的部分
            for (int i = 0; i < flag; i++) {
                if (array[i] > array[j]) {
                    temp = array[j];
                    System.arraycopy(array, i, array, i + 1, j - i);
                    array[i] = temp;
                    flag = j + 1;
                }
            }
        }
    }

    /***
     *@Description 二分插入排序
     *@Param []
     *@Return void
     *@Author steve
     */
    public static void bisectionInsertSort() {
        int flag = 0;
        int temp;
        for (int i = flag + 1; i < array.length; i++) {
            int result = bisectionSearch(flag, array[i]);
            temp = array[i];
            System.arraycopy(array, result, array, result + 1, i - result);
            array[result] = temp;
            flag = flag + 1;

        }
    }

    /***
     *@Description 二分查找
     *@Param [flag:排好序的位置, num:待排序数]
     *@Return int
     *@Author steve
     */
    public static int bisectionSearch(int flag, int num) {

        int start = 0;
        int end = flag;
        int mid;
        //通过while循环,最后一定可以end-start<=1,也就是说要插入的元素一定在start左边,或者等于start,或者在start和end之间
        while (end >= start) {
            //由于这里mid的计算方式,导致要插入的元素不可能存在end-start=1,且end=要插入的元素的情况
            mid = (start + end) / 2;
            if (array[mid] > num) {
                end = mid - 1;
            } else if (array[mid] <= num) {
                start = mid + 1;
            }
        }
        return start;
    }

    public static void main(String[] args) {
//        insertSort();
        bisectionInsertSort();
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i]);
        }
    }
}

上面的写法可能有的胖友对arraycopy比较好奇,大家可以自己写个demo试一下这个方法有什么用,我也是参考java8中ArrayList的sort方法写的,他就是用这个函数搞的。至于这个函数的性能和普通的遍历的性能对比,我也不太清楚,不过这个方法是一个native方法,性能应该还可以。

总结

  写这种算法,思路一定要清晰,通过实验是不管用的,思路清晰了,很快就可以写出来,不然写的一定很复杂。

  

原文地址:https://www.cnblogs.com/gunduzi/p/13256187.html