理解插入排序,希尔排序,选择排序的算法原理

Posted 我是攻城师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解插入排序,希尔排序,选择排序的算法原理相关的知识,希望对你有一定的参考价值。

在前面的文章中,其实已经把效率比较高的排序算法给分析过了,比如比较通用的快排,归并排序和堆排,还有用于特定场景的计数排序等。本篇我们把剩下的几种效率一般的排序算法给介绍一下,分别是插入排序,希尔排序和选择排序。

插入排序(Insert Sort)

插入排序是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

这里我们假如有个无序数组如下,这里为了突出其排序规律,我们故意采用了一组降序的数字:

 
   
   
 
  1. 10,9,8,7,6

排序的过程如下:

 
   
   
 
  1. [9, 10, 8, 7, 6]

  2. [8, 9, 10, 7, 6]

  3. [7, 8, 9, 10, 6]

  4. [6, 7, 8, 9, 10]

从上面可以看到,每个元素会被依次加入上前面的有序序列,反复如此,直到全部排完序。

代码如下:

 
   
   
 
  1.    public static void sort(int[] numbers){

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

  3.            //取遍历的每一个元素

  4.            int currentNumber = numbers[i];

  5.            int j = i - 1;

  6.            //每一个元素,都会与前面所有的有序元素集合进行比较

  7.            //从而交换自己的位置

  8.            while (j >= 0 && numbers[j] > currentNumber) {

  9.                numbers[j + 1] = numbers[j];

  10.                j--;

  11.            }

  12.            //找到当前元素的位置,然后交换。

  13.            numbers[j + 1] = currentNumber;

  14.            System.out.println(Arrays.toString(numbers));

  15.        }

  16.    }

不难发现,对于n个元素的排序,插入排序是需要n*(n-1)次比较和交换的,所以其平均的时间复杂度为О(n²),空间复杂度属于原地排序,只需要一个额外的变量,所以是O(1),此外这种算法属于稳定性算法,不会改变相等元素的相对位置。

希尔排序 (Shell Sort)

希尔排序也称递减增量排序算法或,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。

在插入排序中,每个排序一个元素,需要经过若干次交换才能归位,所以在希尔排序中,采用了步长来优化,这中方法每次可以交换两个元素,在最后步长等于1的时候,会退化成插入排序,但这个时候元素的位置基本有序,只需要执行少量的移动和交换即可完成排序,所以算是对插入排序的一种优化。

给定一个无序数组:

 
   
   
 
  1. 10,9,8,7,6

这里我们按数组的长度除以2来获取步长,则等于5/2=2;

那么则先进行

8 比 10 交换

7 比 9 交换

6 比 8 交换

最终必定会执行一次步长为1,也就是退化成插入排序,步骤如下:

 
   
   
 
  1. [8, 9, 10, 7, 6]

  2. [8, 7, 10, 9, 6]

  3. [6, 7, 8, 9, 10]

  4. [6, 7, 8, 9, 10]

上面的是根据步长进行的比较交换,我们可以发现规律,这两个比较的数字的索引间距是2。

第一轮交换后,数组一部分已经有序,然后步长继续缩减=2/2=1,这个时候,希尔排序彻底退化为插入排序,这个时候虽然也需要量量比较,但是移动次数却大大减少,所以效率相比原生的插入排序要高,但是这个效率与步长的间隔是有关系的,所以希尔排序的平均时间复杂度为:O(nlog^2 n) 平均空间复杂度是O(1),由于需要跨越交换,所以不属于稳定排序。

下面我们看下Java代码如何实现:

 
   
   
 
  1.    public static void sort(int array[]){

  2.        //初始化步长

  3.        int number = array.length / 2;

  4.        //只要步长大于1,就继续排序

  5.        while (number >= 1) {

  6.            //遍历从步长开始的位置元素

  7.            for (int i = number; i < array.length; i++) {

  8.                //取出来当前遍历的元素

  9.                int temp = array[i];

  10.                //得到当前位置减去步长后的下标

  11.                int j = i - number;

  12.                //比较当前值与步长值的大小

  13.                while (j >= 0 && array[j] > temp) {

  14.                    array[j + number] = array[j];

  15.                    j = j - number;

  16.                }

  17.                //赋值交换

  18.                array[j + number] = temp;

  19.                System.out.println(Arrays.toString(array));

  20.            }

  21.            //步长继续缩小,最终一定是1,整个算法退化成插入排序

  22.            number = number / 2;

  23.        }

  24.    }

选择排序(Select Sort)

选择排序是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。

选择排序的思想我认为非常容易理解,简单的说就是,每次找到数组中的最小值,然后放入对应的位置,因为条件是最小值,所以元素本身就在正确的位置,就不会移动,减少了移动的次数。

给定无序数组:

 
   
   
 
  1. 10,19,8,7,1

其排序步骤如下:

 
   
   
 
  1. [1, 19, 8, 7, 10]

  2. [1, 7, 8, 19, 10]

  3. [1, 7, 8, 19, 10]

  4. [1, 7, 8, 10, 19]

这里面可以看到第一次找到最小值1,然后与数组开始的10交换,第二次在剩下的数组中找到了7是最小值,然后与第2位置的19交换,第三次发现8在剩下元素中就是正确的位置,最后19与10交换,至此就完成了整个排序。

这里面的平均时间复杂度为О(n²),因为每一次查询最小值,都需要在剩下的数组中遍历一次,对于n个元素,需要n-1轮的比较查找。

其平均空间复杂度为O(1),这里面仅仅需要一个辅助元素即可。

选择排序采用数组实现的方式为非稳定排序方式。

代码如下:

 
   
   
 
  1.    public static  void sort(int arr[]){

  2.        int min;//存储每次遍历得到的最小元素的数组下标值.

  3.        int swap;//存储代待交换元素的值.

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

  5.            //依次数组的下标赋值.

  6.            min = i;

  7.            //遍历剩余部分的元素,找到最小值的下标.

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

  9.                // 比较元素值.

  10.                if (arr[j] < arr[min]) {

  11.                    min = j;//下标赋值.

  12.                }

  13.            }

  14.            //每次把最小值提前.

  15.            swap = arr[min];

  16.            arr[min] = arr[i];

  17.            arr[i] = swap;

  18.            System.out.println(Arrays.toString(arr));

  19.        }

  20.    }

总结:

本文主要了介绍了插入排序,希尔排序,选择排序的算法原理和思想,尽管这些排序算法并不是最优的选择,并不适合大数据量集下的排序,但是了解这些算法的基本思想还是很有必要的。


以上是关于理解插入排序,希尔排序,选择排序的算法原理的主要内容,如果未能解决你的问题,请参考以下文章

希尔排序算法

经典排序算法系列之插入排序和希尔排序----小白理解(python)

《算法》笔记 3 - 选择排序插入排序希尔排序

排序算法学习(直接插入排序,希尔排序,选择排序,堆排序,冒泡排序)

常用算法之----选择排序插入排序和希尔排序

python包含啥算法