内部排序算法:直接插入排序

基本思想

假设待排序的记录存放在数组R[0..n-1]中。初始时,R[0]自成1个有序区,无序区为R[1..n-1]。 从i=1起直至i=n-1为止,依次将R[i]插入当前的有序区R[0..i-1]中,生成含n个记录的有序区。

算法实现

直接插入排序算法,Java实现,代码如下所示:

01 public abstract class Sorter {
02 public abstract void sort(int[] array);
03 }
04
05 public class StraightInsertionSorter extends Sorter {
06
07 @Override
08 public void sort(int[] array) {
09 int tmp;
10 for (int i = 1; i < array.length; i++) {
11 tmp = array[i]; // array[i]的拷贝
12 // 如果右侧无序区第一个元素array[i] < 左侧有序区最大的array[i-1],
13 // 需要将有序区比array[i]大的元素向后移动。
14 if (array[i] < array[i - 1]) {
15 int j = i - 1;
16 while (j >= 0 && tmp < array[j]) { // 从右到左扫描有序区
17 array[j + 1] = array[j]; // 将左侧有序区中元素比array[i]大的array[j+1]后移
18 j--;
19 }
20 // 如果array[i]>=左侧有序区最大的array[i-1],或者经过扫描移动后,找到一个比array[i]小的元素
21 // 将右侧无序区第一个元素tmp = array[i]放到正确的位置上
22 array[j + 1] = tmp;
23 }
24 }
25 }
26 }

直接插入排序算法,Python实现,代码如下所示:

01 class Sorter:
02 '''
03 Abstract sorter class, which provides shared methods being used by
04 subclasses.
05 '''
06 __metaclass__ = ABCMeta
07
08 @abstractmethod
09 def sort(self, array):
10 pass
11
12 class StraightInsertionSorter(Sorter):
13 '''
14 Straight insertion sorter
15 '''
16 def sort(self, array):
17 i = 0
18 length = len(array)
19 while i<length -1:
20 k = i
21 j = i
22 while j<length:
23 if array[j]<array[k]:
24 k = j
25 j = j + 1
26 if k!=i:
27 array[k], array[i] = array[i], array[k]
28 i = i + 1

排序过程

直接插入排序的执行过程,如下所示:

  1. 初始化无序区和有序区:数组第一个元素为有序区,其余的元素作为无序区。
  2. 遍历无序区,将无序区的每一个元素插入到有序区正确的位置上。具体执行过程为:
    每次取出无序区的第一个元素,如果该元素tmp大于有序区最后一个元素,不做任何操作;
    如果tmp小于有序区最后一个元素,说明需要插入到有序区最后一个元素前面的某个位置,从后往前扫描有序区,如果有序区元素大于tmp,将有序区元素后移(第一次后移:tmp小于有序区最大的元素,有序区最大的元素后移覆盖无序区第一个元素,而无序区第一个元素的已经拷贝到tmp中;第二次后移:tmp小于有序区从后向前第二个的元素,有序区从后向前第二个元素后移覆盖有序区最大元素的位置,而有序区最后一个元素已经拷贝到无序区第一个元素的位置上;以此类推),直到找到一个元素比tmp小的元素(如果没有找到,就插入到有序区首位置),有序区后移操作停止。
  3. 接着,将tmp插入到:从有序区由前至后找到的第一个比tmp小的元素的后面即可。此时,有序区增加一个元素,无序区减少一个元素,直到无序区元素个数为0,排序结束。

下面,通过实例来演示执行直接插入排序的过程,假设待排序数组为array = {94,12,34,76,26,9,0,37,55,76,37,5,68,83,90,37,12,65,76,49},数组大小为20,则执行排序过程如下所示:

  1. 初始有序区为{94},无序区为{12,34,76,26,9,0,37,55,76,37,5,68,83,90,37,12,65,76,49}。
  2. 对于array[1] = 12(无序区第一个元素):临时拷贝tmp = array[1] = 12,tmp = 12小于有序区{94}最后一个元素(94),因为有序区只有一个元素,所以将tmp插入到有序区首位置,此时,有序区为{12,94},无序区为{34,76,26,9,0,37,55,76,37,5,68,83,90,37,12,65,76,49}。
  3. 对于array[2] = 34(无序区第一个元素):临时拷贝tmp = array[2] = 34,tmp = 34小于有序区{12,94}最后一个元素(94),将94后移覆盖array[2],亦即:array[2] = 94;继续将tmp = 34与有序区{12,94}从后向前第二个元素比较,tmp = 34 > 12,则直接将tmp = 34插入到12后面的位置。此时,有序区为{12,34,94},无序区为{76,26,9,0,37,55,76,37,5,68,83,90,37,12,65,76,49}。
  4. 对于array[3] = 76(无序区第一个元素):临时拷贝tmp = array[3] = 76,tmp = 76小于有序区{12,34,94}最后一个元素(94),将94后移覆盖array[3],亦即:array[3] = 94;继续将tmp = 76与有序区{12,34,94}从后向前第二个元素比较,tmp = 76 > 34,则直接将tmp = 76插入到34后面的位置。此时,有序区为{12,34,76,94},无序区为{26,9,0,37,55,76,37,5,68,83,90,37,12,65,76,49}。

……
依此类推执行,直到无序区没有元素为止,则有序区即为排序后的数组。

算法分析

  • 时间复杂度
  1. 最好情况:有序

通过直接插入排序的执行过程可以看到,如果待排序数组恰好为有序,则每次从大小为n-1的无序区数组取出一个元素,和有序区最后一个元素比较,一定是比最后一个元素大,需要插入到有序区最后一个元素的后面,也就是原地插入。
可见,比较次数为n-1次,数组元素移动次数为0次。

最坏情况:逆序

每次从无序区取出第一个元素,首先需要与有序区最后一个元素比较一次,然后继续从有序区的最后一个元素比较,直到比较到有序区第一个元素,然后插入到有序区首位置。
每次从无序区取出第一个元素,移动放到拷贝tmp中,然后再将tmp与有序区元素比较,这个比较过程一共移动的次数为:有序区数组大小,最后还要将拷贝tmp移动插入到有序区的位置上。
在这个过程中:
有序区数组大小为1时,比较2次,移动3次;
有序区数组大小为2时,比较3次,移动4次;
……
有序区数组大小为n-1时,比较n次,移动n+1次。
可见:
比较的次数为:2+3+……+n = (n+2)(n-1)/2
移动的此时为:3+4+……+n+1 = (n+4)(n-1)/2

通过上面两种情况的分析,直接插入排序的时间复杂度为O(n2)。

  • 空间复杂度

在直接插入排序的过程中,只用到一个tmp临时存放待插入元素,因此空间复杂度为O(1)。

  • 排序稳定性

通过上面的例子来看:
当有序区为{0,9,12,26,34,37,55,76,94},无序区为{76,37,5,68,83,90,37,12,65,76,49}的时候,执行下一趟直接插入排序:
对于array[9] = 76(无序区第一个元素):
临时拷贝tmp = array[9] = 76,tmp = 76小于有序区{0,9,12,26,34,37,55,76,94}最后一个元素(94),将94后移覆盖array[9],亦即:array[9] = 94;继续将tmp = 76与有序区{0,9,12,26,34,37,55,76,94}从后向前第二个元素(76)比较,tmp = 76 = 76,则直接将tmp = 76插入到有序区数组元素76后面的位置。此时,有序区为{0,9,12,26,34,37,55,76,76,94},无序区为{37,5,68,83,90,37,12,65,76,49}。
继续执行直至完成的过程中,对于两个相等的数组元素,原始为排序数组中索引位置的大小关系并没有发生改变,也就是说,对于值相等的元素e,存在ei1,ei2,……eik,其中i1,i2……ik是数组元素e在为排序数组中的索引位置,排序前有i1<i2<……<ik,排序后仍然有j1<j2<……<jk,其中j1<j2<……<jk为排序后值相等的元素e的索引。
可见,直接插入排序是稳定的。

时间: 2024-09-28 16:18:03

内部排序算法:直接插入排序的相关文章

常用内部排序算法之五:希尔排序

前言 在前面介绍常用内部排序算法的文章中,我们知道在简单排序算法中,直接插入排序算法的时间复杂度是要优于冒泡排序和简单选择排序的.而这里要讲的希尔排序则是优于直接插入排序的.而且希尔排序打破了原来简单排序中时间复杂度不能超过O(n2)的神话.这也是希尔排序伟大的地方,而且希尔排序不同于之前三种简单排序,希尔排序是以一个科学家的名字进行命名的. 希尔排序的原理 由于希尔排序是在直接插入排序的基础上进行改进的,所以为了更好理解希尔排序,需要再次将直接插入排序请出来.我们知道直接插入排序的基本思想是将

常用内部排序算法之二:快速排序

前言 快速排序可以说是内部排序算法中的高手,之所以称为快速排序,是因为快速排序算法从整体性能上讲是排序冠军.快速排序算法的思想是:通过一趟快速排序将待排序的记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的记录的关键字小,则可分别对这两部分记录继续进行排序,达到整个记录有序.实现快速排序算法的核心是partition函数,这个函数的主要目的先选取当中的一个关键字(称为枢轴),然后尽可能将他放在一个位置,使得它左边的值都比它小,右边的值比它大. 快速排序算法实现 package com.

基于C++实现的各种内部排序算法汇总_C 语言

提起排序算法相信大家都不陌生,或许很多人已经把它们记得滚瓜烂熟,甚至随时可以写出来.是的,这些都是最基本的算法.这里就把各种内部排序算法总结归纳了一下,包括插入排序(直接插入排序,折半插入排序,希尔排序).交换排序(冒泡排序,快速排序).选择排序(简单选择排序,堆排序).2-路归并排序.(另:至于堆排序算法,前面已经有一篇文章针对堆排序的算法实现做了详细的描述) C++实现代码如下: /*******************************************************

C++实现八个常用的排序算法:插入排序、冒泡排序、选择排序、希尔排序等_C 语言

本文实现了八个常用的排序算法:插入排序.冒泡排序.选择排序.希尔排序 .快速排序.归并排序.堆排序和LST基数排序 首先是算法实现文件Sort.h,代码如下: /* * 实现了八个常用的排序算法:插入排序.冒泡排序.选择排序.希尔排序 * 以及快速排序.归并排序.堆排序和LST基数排序 * @author gkh178 */ #include <iostream> template<class T> void swap_value(T &a, T &b) { T t

常用内部排序算法之四:简单选择排序、直接插入排序和冒泡排序

前言 之所以把这三类算法放在一块,是因为除此之外的算法都是在这三类算法的基础上进行优化的.简单选择排序的思想是每一趟n−i+1(i=1,2,...,n−1)个记录中选择最小的记录作为有序序列的第i个记录.直接插入排序的思想是将一个记录插入到已经排好序的有序序列中,从而得到一个新的.记录数增加1的有序表.冒泡排序的算法思想是不断在交换,通过交换完成最终的排序,每一趟的交换就会把最大的记录取出来,下一趟则会把第二大的记录取出来,这样每进行一趟交换就把一个记录取出来的过程称为冒泡. 简单选择排序算法

内部排序算法的总结

内部排序总结 这篇博文我们简要地总结下各种内部排序方法. 这10种排序算法中,前面7种属于建立在"比较"基础上的排序算法,通过决策树已经证明,任何基于比较进行的排序算法的时 间复杂度不可能再优于O(n*logn).后面3种不是建立在比较的基础上的,因此,可以达到线性运行时间. 下面我们给出各种排序方法的时空复杂度的表格(属于自己总结,有不对的地方,希望大家指正或补充). 返回栏目页:http://www.bianceng.cnhttp://www.bianceng.cn/Program

内部排序算法:希尔排序

基本思想 先取一个小于n的整数d1作为第一个增量,把待排序的全部记录分成dx个组.所有距离为d1的倍数的记录放在同一个组中. 先在各组内进行直接插人排序. 然后,取第二个增量d2<d1重复上述的分组和排序. 直至所取的增量dt=1(dt<dt-x<-<d2<d1),即所有记录放在同一组中进行直接插入排序为止. 算法实现 希尔排序算法,Java实现,代码如下所示: 01 public abstract class Sorter { 02 public abstract void

【排序算法】插入排序

本篇博客的主要内容是插入排序.常用的插入排序方法有直接插入排序.折半插入排序.表插入排序和希尔排序.这里介绍两个,直接插入排序和希尔排序. 在介绍排序算法时,我将从基本思想.实例讲解.代码理解三个方面整理. [直接插入排序] 1.基本思想 直接插入排序(Straight Insertion Sorting)的基本思想是一次将每个记录插入到一个已排好序的有序表中去,从而得到一个新的.记录数增加1的有序表. 2.具体做法 我们把第一个序列元素看成是已经排好序的一个记录,后面的元素依次与已排好序的序列

我的Java开发学习之旅------&amp;gt;Java经典排序算法之插入排序

一.算法原理 插入排序法:所谓插入排序法乃是将一个数目插入该占据的位置. 假设我们输入的是 "53,27,36,15,69,  42" 我们从第二个数字开始,这个数字是27,我们的任务只要看看27有没有正确的位置,我们的做法是和这个数字左边的数字来比,因此我们比较27和53,27比53小,所以我们就交换27和53,原来的排列就变成了"27, 53, 36, 15, 69, 42 " 接下来,我们看第3个数字有没有在正确的位置.这个数字是36,它的左边数字是53,36