vlambda博客
学习文章列表

面试必备:经典算法动画解析之插入排序

哈喽,我是「程序员大鹏」。

前面我们介绍了冒泡排序和选择排序,今天我们来看一下简单排序中的插入排序。

打过扑克的都知道,在抓牌的时候,我们不会等抓完所有的牌再用冒泡或者选择排序再理牌。一般是拿到一张牌就放到手里,抓到第二张牌的时候,再跟手里面已经有的牌进行比较,插到合适的位置,然后抓第三张牌,再与手里面的两张牌进行比较,然后再把牌插到合适的位置。这种一边抓牌,一边理牌的方式,我们就称之为「直接插入排序」。

插入排序思想

插入排序是一种从序列左端开始依次对数据进行排序的算法。在排序过程中,索引左侧的数据陆续归位,而右侧留下的就是还没有被排序的数据。

插入排序的思路就是从右侧的未排序区域内取出一个数据做为待排序数据,然后将它插入到已排序区域内合适的位置上。一直到未排序清空。

排序原理

  1. 将待排序的值临时移除并保存在一个临时变量中。
  2. 将空隙左侧的每一个值一次与临时变量比较,如果左边的数大于临时变量的值,则该值右移一格,如果左边的数字更小,就不需要继续比较,本轮操作到此结束。最终左侧会有一个位置的空隙。
  3. 将临时移走的数据插入到当前空隙。
  4. 重复前面三步,直到所有元素均排序完毕。

插入排序动画


插入排序代码

  public static void insertSort(int[] arr) {
//    默认第一个元素是有序的,所以索引不是从0开始,从1开始
    for (int i = 1; i < arr.length; i++) {
      int temp = arr[i];//待排序元素赋值给临时变量
      int position = i;//记录当前比较的索引
      while (position > 0 && arr[position - 1] > temp) {
        arr[position] = arr[position - 1];
        position--;
      }
      arr[position] = temp;//插入到正确的位置
    }
  }

让我们来一行一行地分析一下代码:

for (int i = 1; i < arr.length; i++) {

在上面代码是外层循环,在i的左侧是已排序数据,起始的i等于1,是默认第0个数据已经是排序数据。

int temp = arr[i];//待排序元素赋值给临时变量
int position = i;//记录当前比较的索引

每一轮开始的时候,position是待排序数据的索引,temp的值就是本轮待排序的数据,第一轮的待排序索引为1,第二轮是2,第三轮是3,依次类推,一直到最后一轮。

while (position > 0 && arr[position - 1] > temp) {

开始内层的循环,如果满足position大于0,并且当前位置的数据与temp比较,如果大于temp数据,则进入到循环里面。

        arr[position] = arr[position - 1];
        position--;

数据平移一个位置,将position做为空隙。

arr[position] = temp;

将待排序的数据temp插入到空隙的位置。

排序步骤分析

下面我们应用插入排序对数组[4,2,3,5,1]进行排序,来看每一步的操作。

第0轮

这一轮其实是没有的,我们默认就是第一个位置的值是有序的。

第一轮

将索引值为1的值2暂时移除,保存在临时变量temp中,此时temp为2。

「第1步」:比较索引0的值4与temp中的

「第2步」:因为4大于2,所以将4右移。

空隙移到了数组最左端,没有其他值可以比较了。

「第3步」:将temp插回到数组中,结束第一轮。

第二轮

将索引为2的值3暂时移除,保存到临时变量temp中,此时temp为3。

「第4步」:比较4与temp

「第5步」:因为4比3大,所以将4右移

「第6步」:比较2与temp

「第7步」:因为2比3小,所以无须2平移,平移结束

「第8步」:将temp插回到数组的空隙中,结束第二轮。

第三轮

将索引为3的值5暂时移除,保存在临时变量temp中,此时temp的值为5.

「第9步」:比较4与temp

「第10步」:因为4比5小,,所以无须4平移,平移结束

第四轮

将索引为4的值1暂时移除,保存在临时变量temp中,此时temp的值为1.

「第11步」:比较5与temp

「第12步」:因为5比1大,所以将5右移

「第13步」:比较4与temp

「第14步」:因为4比1大,所以将4右移

「第15步」:比较3与temp

「第16步」:因为3比1大,所以将3右移

「第17步」:比较2与temp

「第18步」:因为2比1大,所以将2右移

空隙移到了数组最左端,没有其他值可以比较了。

「第19步」:将temp的值1插回到数组的空隙中。

到现在为止,整个数组已经排好序了。

选择排序复杂度分析

在插入排序中,需要将取出的数据与其左边的数字进行比较。就跟前面讲的步骤一 样,如果左边的数字小的时候,就不需要继续比较,本轮操作到此结束,自然也不需要对数字进行平移。

但是,如果取出的数字比左边已排序的数字都要小,就必须不停地比较大小,交换数字,直到它到达整个序列的最左边为止。具体来说,就是第 k 轮需要比较k-1 次。因 此,在最糟糕的情况下,第2 轮需要操作1 次,第 3 轮操作2 次……第n 轮操作n-1 次,所以时间复杂度和冒泡排序的一样,都为 O(n^2)。

和前面讲的排序算法一样,输入数据按从大到小的顺序排列时就是最糟糕的情况。