icode icode
首页
  • Android学习

    • 📁基础内容
    • 📺AndroidCore
    • 🎨Android-UI
    • 🏖️Components
    • 📊Fragment
    • 🔗网络操作
    • 🔏异步机制
    • 📦数据存储
    • 🗃️Gradle
  • 学习笔记

    • 『框架』笔记
    • 『Kotlin』笔记
    • 《Vue》笔记
    • 《Git》学习笔记
    • 『Bug踩坑记录』
  • ListView
  • RecyclerView
  • ViewPager
  • Java笔记

    • 🟠JavaSE
    • 🟢JavaWeb
    • 🔴JavaEE
    • ⚪JavaTopic
    • 🍳设计模式
  • 计算机基础

    • 📌计算机网络
    • 🔍数据结构
    • 📦数据库
    • 💻OS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • 关于

    • 📫关于我
  • 收藏

    • 网站
    • 资源
    • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

iqqcode

保持对技术的探索实践与热爱
首页
  • Android学习

    • 📁基础内容
    • 📺AndroidCore
    • 🎨Android-UI
    • 🏖️Components
    • 📊Fragment
    • 🔗网络操作
    • 🔏异步机制
    • 📦数据存储
    • 🗃️Gradle
  • 学习笔记

    • 『框架』笔记
    • 『Kotlin』笔记
    • 《Vue》笔记
    • 《Git》学习笔记
    • 『Bug踩坑记录』
  • ListView
  • RecyclerView
  • ViewPager
  • Java笔记

    • 🟠JavaSE
    • 🟢JavaWeb
    • 🔴JavaEE
    • ⚪JavaTopic
    • 🍳设计模式
  • 计算机基础

    • 📌计算机网络
    • 🔍数据结构
    • 📦数据库
    • 💻OS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • 关于

    • 📫关于我
  • 收藏

    • 网站
    • 资源
    • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 排序算法

    • 被排序算法吊打系列
    • 冒泡排序,你需要了解的三种优化
    • 选择排序
    • 插入排序
      • 希尔排序
      • 归并排序
      • 快速排序
      • 堆排序
      • 二分查找
      • 计数排序
      • 基数排序
      • 休眠排序
    • 线性表

    • 树

    • 数据结构
    • 排序算法
    iqqcode
    2021-06-17
    目录

    插入排序

    # 1. 选择排序思想

    插入排序(Insertion sort)是一种简单直观且稳定的排序算法。

    插入排序的方式非常像我们整理扑克牌一样。我们每次拿起一张牌并将它插入牌中正确的位置。为了找到一张牌的正确位置,我们从右到左将它与已在手中的每张牌进行比较,如下图所示:

    # 2. 选择排序详解

    排序原理:

    1. 把所有的元素分为两组,已经排序的和未排序的;
    2. 找到未排序的组中的第一个元素,向已经排序的组中进行插入;
    3. 倒叙遍历已经排序的元素,依次和待插入的元素进行比较,直到找到一个元素小于等于待插入元素,那么就把待 插入元素放到这个位置,其他的元素向后移动一位;

    【排序动画演示】


    我们拿第四趟和第五趟排序详细理解一下:

    【第四趟排序】 首先待插入的数字为6,前面的4,6,7,8已经有序。我们将数字6放入到子序列4,6,7,8中合适的位置。从右向左倒序遍历4,6,7,8(因为是递增排序),元素6先和子序列中的8比较,发现自己比8小,6和8交换位置。

    交换完之后,6继续和前面的4,6,7元素逐个比较。

    直到比较到6时,发现已经等于自己了,则此趟排序结束。

    【第五趟排序】

    直到将2放到子序列的第一位

    # 3. 直接插入排序代码实现

    外层循环控制有序元素,内层循环实现待插入元素比较交换

    public void insertSort(int[] arr) {
            // 默认第一个元素有序
            for (int i = 1; i < arr.length; i++) {
                // 待排序元素插入到已排序元素中
                for (int j = i; j > 0; j--) {
                    // 将待排序元素arr[j]倒序依次与有序序列中元素比较,放入有序序列中合适位置
                    if(arr[j] < arr[j-1]) {
                        int temp = arr[j];
                        arr[j] = arr[j-1];
                        arr[j-1] = temp;
                    } else {
                        // 有序则不做处理(等同于break),此处不加else也可以
                        break;
                    }
                }
            }
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    测试

    [1, 2, 4, 6, 6, 7, 8, 9]

    # 4. 折半插入排序代码实现

    其实,这就是将待排序元素通过二分查找放到已有序的子序列,更快速的定位定位插入的位置。

    二分查找的前提就是有序的子序列查找,折半插入排序在数据集无序的情况下要优于直接插入排序,但是在近乎有序的数据集下,由于插入排序只比较一次,因此最好情况下的直接插入排序要优于折半插入排序。

    折半插入与直接插入区别: 这里的查找操作的时间复杂度为O(n)量级。但是如果使用二分查找在数组 arr 的 [0,i - 1] 的范围内查找关键字 key ,那么就可以将查找操作的时间复杂度降到O(logn)量级.

    public int[] insert(int[] arr) {
        for (int i = 1; i < arr.length; i++) {
            int index = binary_search(arr, arr[i]);
            for (int j = i; j > index; j--) {
                if (arr[j] < arr[j - 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j - 1];
                    arr[j - 1] = temp;
                }
            }
        }
        return arr;
    }
    
    public int binary_search(int[] arr, int target) {
        int left = 0, right = arr.length - 1;
        while(left < right) {
            int mid = (left + right) >>> 1;
            if(target > arr[mid]) {
                left = mid + 1;
            } else {
                right = mid;
            }
        }
        return left;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26

    测试

    [1, 2, 4, 6, 6, 7, 8, 9]

    # 5. 复杂度分析

    【时间复杂度】

    插入排序使用了双层for循环,其中内层循环的循环体是真正完成排序的代码,所以,我们分析内层循环体的执行次数即可。

    最坏情况,也就是待排序的数组元素为逆序 {9,8,7,6,6,4,2,1}

    那么:比较的次数为:(N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1) * (N-1) / 2 = N^2/2-N/2;

    交换的次数为:(N-1)+(N-2)+(N-3)+...+2+1=((N-1)+1) * (N-1)/2 = N^2/2-N/2;

    总执行次数为:(N ^ 2/2 - N/2) + (N ^ 2 / 2-N/2)=N ^ 2 - N;

    根据大O推导法则,插入排序的时间复杂度为O(N^2)

    【空间复杂度】 O(1)

    编辑 (opens new window)
    上次更新: 2021/06/27, 10:49:09
    选择排序
    希尔排序

    ← 选择排序 希尔排序→

    最近更新
    01
    匿名内部类
    10-08
    02
    函数式接口
    10-08
    03
    ARouter-Kotlin踩坑
    10-05
    更多文章>
    Theme by Vdoing | Copyright © 2021-2023 iqqcode | MIT License | 备案号-京ICP备2021028793号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式
    ×