深入浅出排序算法之直接插入排序(拓展:折半插入排序)

目录

[1. 图示解析](#1. 图示解析)

[2. 原理解析](#2. 原理解析)

[3. 代码实现](#3. 代码实现)

[4. 性能分析](#4. 性能分析)

[5. 折半插入排序(拓展)](#5. 折半插入排序(拓展))


直接插入排序和选择排序的第一趟就是第一个关键字

1. 图示解析

2. 原理解析

整个区间被分为:① 有序区间;② 无序区间

每次选择无序区间的第一个元素,在有序区间内选择合适的位置插入。

为了各位小伙伴能更加清楚地认识直接插入排序,我接下用文字描述直接插入排序的过程!

想通过一个例子来体会一下插入排序的执行流程。例如,对原始序列{49,38,65,97,76,13,27,++49++}进行直接插入排序的具体流程如下(序列中有两个49,其中一个加下划线,加以区分)。

原始序列:49 38 65 97 76 13 27 ++49++

(1)一开始只看49,一个数当然是有序的。

有序序列:{49};无序序列:{38 65 97 76 13 27 ++49++}

(2)向有序序列插入38,38 < 49,所以49向后移动一个位置,38插入到49原来的位置,这趟排序后的结果为:

有序序列:{38 49};无序序列:{ 65 97 76 13 27 ++49++}

(3)向有序序列插入65,65 > 49,所以不需要移动,65就应该在49只有,这趟排序后的结果为:

有序序列:{38 49 65};无序序列:{97 76 13 27 ++49++}

(4)插入97,97 > 65,所以不需要移动,97就应该在65之后,这趟排序后的结果为:

有序序列:{38 49 65 97};无序序列:{76 13 27 ++49++}

(5)插入76,76 < 97,所以97向后移动一个位置,继续比较,76 > 65,65不需要移动,76应该插入在65之后,97之前,这趟排序后的结果为:

有序序列:{38 49 65 76 97};无序序列:{13 27 ++49++}

(6)插入13,13 < 97,97后移;13 < 76,76后移;这样逐个向前比较,发现13应该插入在最前面,这趟排序后的结果为:

有序序列:{13 38 49 65 76 97};无序序列:{27 ++49++}

(7)插入27,还是从后面前行比较,确定27应该插入在13之后、38之前,这趟排序后的结果为:

有序序列:{13 27 38 49 65 76 97};无序序列:{++49++}

(8)最后插入++49++ ,同样从后向前逐个比较,知道49 = ++49++ < 65,它的位置确定,直接插入排序全过程完成。最后的排序结果为:

有序序列:{13 27 38 49 ++49++ 65 76 97};无序序列:{}

总结算法思想:

每趟将一个待排序的关键字按照其值的大小插入到已经拍好的部分有序序列的位置上,直到所有待排序关键字都插入到有序序列中为止。
注意!!!!!!!!!!

小伙伴们请注意,我这里省略了i = 0的情况,从i = 1开始,也就是一开始只看49,一个数当然是有序的。如果是在考试中一定要把i = 0的情况作为第一趟(针对专升本和考研都一样)。

3. 代码实现

java 复制代码
    //直接插入排序
    public static void insertSort(int[] arr) {
        //代码可以从i = 1开始算起,但是做题画图时,一定要从i = 0开始算起
        for (int i = 1; i < arr.length; i++) {
            int j = i - 1;
            int tmp = arr[i];
            for (; j >= 0; j--) {
                //如果arr[j] > tmp变成arr[j] >= tmp就变成不稳定了
                if (arr[j] > tmp) {
                    arr[j + 1] = arr[j];
                } else {
                    break;
                }
            }
            arr[j + 1] = tmp;
        }
    }

    public static void main(String[] args) {
        int[] arr = {10,9,8,7,6,5,4,3,2,1};
        Sort.insertSort(arr);
        for (int x : arr) {
            System.out.print(x + " ");
        }

    }

4. 性能分析

|--------|---------|---------|-----------|
| 时间复杂度 ||| 空间复杂度 |
| 最好 | 平均 | 最坏 | 空间复杂度 |
| O(N) | O(N^2) | O(N^2) | O(1) |
| 数据有序 | | 数据逆序 | |

稳定性:稳定

如果一个排序是稳定的,那么就可以实现为不稳定的

但是如果一个算法本身是不稳定的,你没办法实现为稳定的排序

插入排序,原始数据越有序,时间效率越高!

检测一下:

java 复制代码
    //创建升序数组
    public static void createArray1(int[] arr){
        for(int i = 1;i<10000;i++){
            arr[i - 1] = i;
        }
    }
    //创建逆序数组
    public static void createArray2(int[] arr){
        for(int i = 0;i<10000;i++){
            arr[i] = 10000 - i;
        }
    }

    public static void main(String[] args) {
        int[] arr1 = new int[10000];
        Test.createArray1(arr1);
        long s1 = System.currentTimeMillis();
        Sort.insertSort(arr1);
        long e1 = System.currentTimeMillis();
        System.out.println("数组有序的情况:"+(e1 - s1));

        int[] arr2 = new int[10000];
        Test.createArray2(arr2);
        long s2 = System.currentTimeMillis();
        Sort.insertSort(arr2);
        long e2 = System.currentTimeMillis();
        System.out.println("数组逆序的情况:"+(e2 - s2));
    }

5. 折半插入排序(拓展)

在有序区间选择数据应该插入的位置时,因为区间的有序性,可以利用折半查找的思想来增加插入算法的效率!

java 复制代码
    //折半插入排序
    public static void bsInsertSort(int[] arr) {
        //代码可以从i = 1开始算起,但是做题画图时,一定要从i = 0开始算起
        for (int i = 1; i < arr.length; i++) {
            int v = arr[i];
            int left = 0;//左边标记永远从0下标开始
            int right = i;
            while(left < right){
                int mid = (left + right) / 2;
                //需要[left right)
                if(arr[mid] < v){
                    //如果区间要闭,就赋值mid + 1或者mid - 1
                    left = mid + 1;
                }else{
                    //如果右区间要开,就赋值mid
                    right = mid;
                }
            }
            
            for (int j = i; j > left; j--) {
                arr[j] = arr[j - 1];
            }
            arr[left] = v;
        }
    }
相关推荐
好奇龙猫6 分钟前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
sp_fyf_202442 分钟前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
ChoSeitaku1 小时前
链表交集相关算法题|AB链表公共元素生成链表C|AB链表交集存放于A|连续子序列|相交链表求交点位置(C)
数据结构·考研·链表
偷心编程1 小时前
双向链表专题
数据结构
香菜大丸1 小时前
链表的归并排序
数据结构·算法·链表
jrrz08281 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
oliveira-time1 小时前
golang学习2
算法
@小博的博客1 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
南宫生2 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
懒惰才能让科技进步3 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝