排序算法(三)-插入排序

0. 原理

插入排序和冒泡排序以及选择排序的原理也比较相似,不过和他们元素交换的思想有点不同,就是插入不再是元素之间的交换,而是将排序的过程转换为在有序序列中插入元素的过程。具体的流程如下(方便理解,我们默认将序列从小到大排列):

  1. 第一个数字看成是一个有序序列
  2. 取出第二个数字插入到第一个元素中,如果第二个数字大于第一个数字,则数字位置不变,否则则插入到第一个数字前面。
  3. 重复上述过程,取出第三个数字,根据和前两个各个数字的大小比较,判断插入到哪个数字中。
  4. 直到取到倒数第二个数字,排序结束

对[2, 8, 1, 5, 3]这个数组排序,模拟流程如下:

round 1 start
round 1 finish
round 2 start
2 8 1 5 3  (exchange data: 1 <-> 8)  2 1 8 5 3 
2 1 8 5 3  (exchange data: 1 <-> 2)  1 2 8 5 3 
round 2 finish
round 3 start
1 2 8 5 3  (exchange data: 5 <-> 8)  1 2 5 8 3 
round 3 finish
round 4 start
1 2 5 8 3  (exchange data: 3 <-> 8)  1 2 5 3 8 
1 2 5 3 8  (exchange data: 3 <-> 5)  1 2 3 5 8 
round 4 finish
  • 第一轮,由于2,8已经有序,所以不需要交换。
  • 第二轮,由于2,8,1中1的位置不对,因此通过两次交换放到第一个位置(相当于插入过程)

1. 实现

@Override
public int[] sort(int[] data) {
    if (data == null || data.length <= 1) {
        return data;
    }

    for (int i = 1; i < data.length; i++) {
        System.out.println("round " + i + " start");
        for (int j = i; j > 0; j--) {
            //利用相邻比较交换模拟了插入的过程
            if (data[j] < data[j - 1]) {
                swap(data, j, j - 1);
            } else {
                break;
            }
        }
        System.out.println("round " + i + " finish");
    }
    return data;
}

代码结构和冒泡排序与选择排序很类似,两个循环,只不过内循环的比较和起止不一样。关键在于j=i这个开始序列设置,相当于将原来的有序序列往后扩展一个数字,而内循环的操作则有点类似于冒泡算法的内循环,两两相邻比较交换(这一块其实优化,下面会讲到)。

具体实现代码可看:

GitHub/InsertSort.java

2. 复杂度

外层循环遍历n-1次,内层循环最好只需要一次判断,最差需要i次交换,数据比较一次,数据操作三次。
因此,最好的情况是序列已经是升序,则复杂度为O(n),最坏的情况是序列是逆序,需要O(n²)此排序,平均时间负责度为O(n²)。空间复杂度为O(1)。

3. 优化

插入排序很直观一个可以优化的点就是插入的过程,因为已知序列是一个有序序列,因此可以通过二分查找法找到适合插入的位置,虽然数组的移动还是需要同样的次数,但是比较的次数会显著减少。具体实现如下:

@Override
public int[] sort(int[] data) {
    if (data == null || data.length <= 1) {
        return data;
    }

    for (int i = 1; i < data.length; i++) {
        System.out.println("round " + i + " start");
        int insertIndex = binarySearch(data, i);
        for (int j = i; j > insertIndex; j--) {
            swap(data, j, j - 1);
        }
        System.out.println("round " + i + " finish");
    }
    return data;
}

private int binarySearch(int[] data, int i) {
    int start = 0;
    int end = i - 1;
    while (start <= end) {
        int middle = (start + end) / 2;
        if (data[i] < data[middle]) {
            end = middle - 1;
        } else {
            start = middle + 1;
        }
    }
    return start;
}

具体实现代码可看:

GitHub/InsertSort.java

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值