• 数据结构与算法(java版)第二季 - 2 插入排序


    目录

    概念

    逆序对

    插入排序的优化---将交换变成是挪动

    二分搜索

    插入排序 – 二分搜索优化

     二分搜索实现插入排序代码的实现


    概念

    插入排序非常类似于扑克牌的排序

    如上图所示,就像是在前面已经是拍好顺序的,后面的是需要进行新的排序。

    执行流程是如下所示:

    ①在执行的过程之中,插入排序是分为2各部分:头部是已经进行排序好之后,尾部是需要进行相应的排序的。

    ②从头开始扫描每一个元素:每当扫描到一个元素,就是将它插入到头部合适的位置,使得头部是有序的。

    理解的过程是最开始的地方的第一张牌我是抓在手里的,从下标为1开始不断的进行抓牌的过程.之后后面的牌逐渐的跟前面的牌进行一个比较,直到相应的牌的顺序是一致的.

    1. public class InsertSort extends Comparable> extends Sort{
    2. protected void sort() {
    3. for(int begin = 1;begin < array.length; begin++)
    4. {
    5. int cur = begin;
    6. while(cmp(cur,cur-1) < 0 && cur > 0)//这里是需要一个相应的cur>0
    7. {
    8. swap(cur,cur-1);
    9. //进行相应的交换之后,是需要将原来的数量进行一个减一的过程,依次跟前面进行一个比较
    10. cur--;
    11. }
    12. }
    13. }
    14. }

    逆序对

    什么叫做逆序对????         数组<2,3,8,6,1>之中的逆序对为:<2,1><3,1><8,1><8,6><6,1>,共有五个逆序对.

    插入排序的时间复杂度与逆序对的数量成正比关系
    逆序对的数量越多,插入排序的时间复杂度越高

     如上图所示的逆序对,可以得知

    最坏、平均时间复杂度: O(n^ 2 )[就是上面的左下角的三角形的面积]
    最好时间复杂度: O(n)[如果要是没有逆序对,相应的数组是升序的过程]
    空间复杂度: O(1)
    属于稳定排序
    当逆序对的数量是极少的时候,插入排序的效率是非常高的,甚至速度是比相应的o(nlogn)级别的快速排序还是要快的.

    插入排序的优化---将交换变成是挪动

     如上所示:思路是将原始的交换过程变成是相应的一个挪动的过程.

    ①先将原始想要进行插入的元素使用一个备份,将其保存下来

    ②头部有序数据之中比待插入元素大的,都朝着尾部方向移动一个位置

    ③讲待插入的元素放入合适的位置

    优化的代码是如下所示:

    1. public class InsertSort extends Comparable> extends Sort{
    2. protected void sort() {
    3. //优化的过程---将交换变成挪动的过程
    4. for(int begin = 1;begin < array.length; begin++)
    5. {
    6. int cur = begin;
    7. Integer v = array[cur];
    8. while(cur > 0 && cmp(v,array[cur]- 1) < 0)
    9. {
    10. array[cur] = array[cur - 1];
    11. cur--;
    12. }
    13. array[cur] = v;
    14. }
    15. }
    16. }

    通过进行一个验证可以知道,优化并不是很明显的,也就是说进入相应的一个while循环是越多的,优化是越明显的.

    二分搜索

    • 概念
    如何确定一个元素在数组中的位置?(假设数组里面全都是整数)
    如果是无序数组,从第 0 个位置开始遍历搜索,平均时间复杂度:O(n)

    如果是有序数组,可以使用二分搜索,最坏时间复杂度:O(logn) 二分搜索的过程就是逐渐进行除以2的过程.

    • 思路

     begin是相当于最初的一个元素的索引,end表示的最后的一个元素的索引+1.

    假设在[begin,end)范围内搜索某个元素v,mid==(begin+end)/2

    ◼ 如果 v < m,去 [begin, mid) 范围内二分搜索
    ◼ 如果 v > m,去 [mid + 1, end) 范围内二分搜索
    ◼ 如果 v == m,直接返回mid

     二分搜索的过程就是进行一个找出位置的过程,简单容易理解.

    • 实例

    •  实现过程与代码
    1. public class BinarySearch {
    2. public static int indexOf(int[] array,int v)
    3. {
    4. if(array == null || array.length == 0) return -1;
    5. int begin = 0;
    6. int end = array.length;
    7. while(begin < end)
    8. {
    9. int mid = (begin + end) >> 1;
    10. if(v < array[mid])
    11. {
    12. end = mid;
    13. }else if(v < array[mid])
    14. {
    15. begin = mid + 1;
    16. }
    17. else
    18. {
    19. return mid;
    20. }
    21. }
    22. return -1;
    23. }
    24. }

    插入排序 – 二分搜索优化

    在元素 v 的插入过程中,可以先二分搜索出合适的插入位置,然后再将元素 v 插入

    要求二分搜索返回的插入位置:第1个大于 v 的元素位置
    如果 v 是 5,返回 2
    如果 v 是 1,返回 0
    如果 v 是 15,返回 7
    如果 v 是 8,返回 5
    • 思路(找到元素v的插入位置)
    假设在 [ begin , end ) 范围内搜索某个元素 v mid == ( begin + end ) / 2
    如果 v < m,去 [ begin , mid ) 范围内二分搜索
    如果 v m,去 [ mid + 1, end ) 范围内二分搜索

     

     插入位置的选择

    1. /**
    2. * 查找v在有序数组array之中待插入的位置
    3. * 在元素v的插入过程,可以使用二分搜索出合适的插入位置,然后再讲元素v插入
    4. * 要求二叉搜索方法返回的插入位置:第一个大于v的元素位置
    5. */
    6. public static int search (int[] array,int v)
    7. {
    8. if(array == null || array.length == 0) return -1;
    9. int begin = 0;
    10. int end = array.length;
    11. while(begin < end)
    12. {
    13. int mid = (begin + end) >> 1;
    14. if(v < array[mid])
    15. {
    16. end = mid;
    17. }else
    18. {
    19. begin = mid + 1;
    20. }
    21. }
    22. return begin;
    23. }

     二分搜索实现插入排序代码的实现

    思路一:不进行封装

    1. public class InsertSort3extends Comparable> extends Sort{
    2. protected void sort() {
    3. for(int begin = 1;begin < array.length; begin++)
    4. {
    5. Integer v= array[begin];
    6. int insertIndex = search(begin);
    7. //将[insertIndex,baegin)范围内的元素向右边挪动一个单位
    8. for(int i = begin; i > insertIndex; i--)
    9. {
    10. array[i] = array[i-1];
    11. }
    12. array[insertIndex] = v;
    13. }
    14. }
    15. /**
    16. * 利用二分搜索找到 index 位置元素的待插入位置
    17. * 已经排号顺序数组的取件范围是 [0,index)
    18. */
    19. private int search(int index)
    20. {
    21. int begin = 0;
    22. int end = index;
    23. while(begin < end)
    24. {
    25. int mid = (begin + end) >> 1;
    26. if(cmp(array[index],array[mid])<0)
    27. {
    28. end = mid;
    29. }else
    30. {
    31. begin = mid + 1;
    32. }
    33. }
    34. return begin;
    35. }
    36. }

    思路二:进行相应的封装

    1. public class InsertSort4extends Comparable> extends Sort{
    2. protected void sort() {
    3. for(int begin = 1;begin < array.length; begin++)
    4. {
    5. insert(begin,search(begin));
    6. }
    7. }
    8. /**
    9. * 将source位置的元素插入到相应的dest之中
    10. */
    11. private void insert(int source,int dest)
    12. {
    13. Integer v = array[source];
    14. for(int i = source; i > dest; i--)
    15. {
    16. array[i] = array[i-1];
    17. }
    18. array[dest] = v;
    19. }
    20. /**
    21. * 利用二分搜索找到 index 位置元素的待插入位置
    22. * 已经排号顺序数组的取件范围是 [0,index)
    23. */
    24. private int search(int index)
    25. {
    26. int begin = 0;
    27. int end = index;
    28. while(begin < end)
    29. {
    30. int mid = (begin + end) >> 1;
    31. if(cmp(array[index],array[mid])<0)
    32. {
    33. end = mid;
    34. }else
    35. {
    36. begin = mid + 1;
    37. }
    38. }
    39. return begin;
    40. }
    41. }

    这里应当注意的是,使用二分搜索之后,知识减少了比较次数,但是插入排序的平均时间复杂度仍然是O(n^2).

  • 相关阅读:
    java基础入门(一)
    【OpenAI】新功能发布
    DevExpress .Net Components 22.1.6 Crack
    【Mybatis源码】源码分析
    Java框架 Spring5--事务
    使用Trivy扫描Docker镜像漏洞详细指南
    包埋紫杉醇的Pluronic P85/聚乳酸(PLA-P85-PLA)纳米粒子|制备方法
    【JavaScript】HTML文件插入JavaScript函数
    MongoDB 7.0 搭建 Sharding 副本集群
    Session会话追踪的实现机制
  • 原文地址:https://blog.csdn.net/m0_47489229/article/details/126445196