被排序算法吊打之—插入排序

被排序算法吊打之—插入排序_第1张图片

1. 选择排序思想

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

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

被排序算法吊打之—插入排序_第2张图片

2. 选择排序详解

排序原理:

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

【排序动画演示】

被排序算法吊打之—插入排序_第3张图片


被排序算法吊打之—插入排序_第4张图片

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

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

被排序算法吊打之—插入排序_第5张图片

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

被排序算法吊打之—插入排序_第6张图片

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

【第五趟排序】

被排序算法吊打之—插入排序_第7张图片

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

被排序算法吊打之—插入排序_第8张图片

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

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

	public static void insertSort(int[] arr) {
        //默认第一个元素有序
        for (int i = 1; i < arr.length; i++) {
            //待排序元素插入到已排序元素中
            for (int j = i; j > 0; j--) {
                //已排序元素与待排序的比较
                if(arr[j] < arr[j-1]) {
                    int temp = arr[j];
                    arr[j] = arr[j-1];
                    arr[j-1] = temp;
                }else {
                    //j之前的元素一定有序,arr[j] !
                    //没有必要继续比较,直接跳出
                    break;
                }
            }
        }
    }

测试

被排序算法吊打之—插入排序_第9张图片

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

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

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

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

折半插入与直接插入区别: 时间、空间、稳定性均与直接插入排序相同,只是元素比较次数不同而已。

	public static void halfInsert(int[] arr) {
        int low , mid, high, j = 0;
        for (int i = 1;i < arr.length; i++) {
            //未排序集合的第 一个元素
            int temp = arr[i];
            //已排序集合的第一个元素
            low = 0;
            //已排序集合的最后一个元素
            high = i - 1;
            while (low <= high) {
                //已排序集合中间元素
                mid = low + (high - low) / 2;
                if (temp > arr[mid]) {
                    low = mid + 1;
                }else {
                    high = mid - 1;
                }
            }
            //要插入的位置在high+1,交换元素
            for (j = i - 1; j > high; j--) {
                arr[j+1] = arr[j];
            }
            arr[j+1] = temp;
        }
    }

测试

被排序算法吊打之—插入排序_第10张图片

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

5. 复杂度分析

【时间复杂度】O(N^2)

插入排序使用了双层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)


《被排序算法吊打系列汇总》

  • 被排序算法吊打之—冒泡排序 你需要了解的三种优化

  • 被排序算法吊打之—选择排序

  • 被排序算法吊打之—插入排序

  • 被排序算法吊打之—希尔排序

  • 被排序算法吊打之—归并排序

  • 被排序算法吊打之—快速排序

  • 被排序算法吊打之—堆排序

你可能感兴趣的:(算法与数据结构,排序算法,数据结构,插入排序)