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

插入排序 原理

  1. 它的基本思想是将数组分为有序区间和无序区间,默认数组第一个元素为有序区间第一个元素,后面的为无序区间。
  2. 使用双重循环,外循环从第一个元素后开始,内循环对当前元素拿去与有序区间进行比较插入。
  3. 先拿当前元素(待排序)与已排好序的数组末位进行比较,若小于数组末位则进行交换,继续向前比较

js实现

insertionSort = (arr) => {
  // 边界判断
  if (arr == null || arr.length < 2) {
    return arr
  }
  for (let i = 1; i < arr.length; i++) {
    // 默认数组第一位元素已经排好序,
    let temp = i // 无序区间首位末位是i,即temp
    // 把无序区间队首拿去和有序区间比较,从有序区间尾开始向前比较,若小于则交换位置继续向前比较,直到temp大于比较的元素结束当前循环
    while (temp > 0) {
      if (arr[temp] < arr[temp - 1]) {
      //es6新语法,可实现无第三方交换位置
        [arr[temp], arr[temp - 1]] = [arr[temp - 1], arr[temp]]
        /* let x = arr[temp]
        arr[temp] = arr[temp - 1]
        arr[temp - 1] = x */
        temp--
      } else {
        break
      }
    }
  }
  return arr
}
let arr = [4, 2, 1, 4, 7, 7, 0, 9]
console.log(insertionSort(arr));

tip

两个元素值互换用了es6的语法

let a = 1,
	b = 2;
[a, b] = [b, a]
// a=2,b=1

算法分析

空间复杂度

  • 在上述插入排序中,当待排序数组是有序时,是最优的情况,只需当前数跟前一个数比较一下就可以了,这时一共需要比较N- 1次,时间复杂度为O(N)
  • 最坏的情况是待排序数组是逆序的,此时需要比较次数最多,总次数记为:1+2+3+…+N-1,所以,插入排序最坏情况下的时间复杂度为O(N^2)
  • 平均来说,A[1…j-1]中的一半元素小于A[j],一半元素大于A[j]。插入排序在平均情况运行时间与最坏情况运行时间一样,是输入规模的二次函数

空间复杂度

  • 空间复杂度为常数阶O(1)
不足
  • 每次只能将数据移动一位,低效。当数据量大或者数据逆序时有些乏力

优化-希尔排序

希尔排序(缩小增量排序)Diminishing Increment Sort

优化依据
  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。
个人理解
  • 先取一个小于n的整数d1作为增量,把元素全部记录分组,所有距离为d1的倍 数为一组。在各组中采取直接插入排序。然后选取第二个增量d2。。。。。。
  • 比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换
  • 算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行分组,在每组中再进行排序。
  • 当增量减到1时,整个要排序的数被分成一组,排序完成。
  • 一般的初次取序列的一半为增量,以后每次减半,直到增量为1。
代码实现
let arr1 = [2, 3, 1, 5, 6, 4, 8, 7]
diminishingIncrementSort = (arr) => {
  let len = arr.length
  // 初始将数组长度的一半设为增量d,每次大循环减半d = d/2,直到增量d为1,即d>0
  for (let d = Math.floor(len / 2); d > 0; d = Math.floor(d / 2)) {
  // 开始内层循环,将数组按照增量d分为若干数组,开始为d个小数组,逐渐变为1个
    for (var i = d; i < len; i++) {
      for (var j = i - d; j >= 0; j -= d) {
      // 插入排序
        if (arr[j] > arr[d + j]) {
          var temp = arr[j];
          arr[j] = arr[d + j];
          arr[d + j] = temp;
        }
      }
    }
  }
  console.log(arr);
  return arr
}
diminishingIncrementSort(arr1)
算法分析

希尔排序中对于增量序列的选择十分重要,直接影响到希尔排序的性能。我们上面选择的增量序列{n/2,(n/2)/2…1}(希尔增量),其最坏时间复杂度依然为O(n^2),
一些经过优化的增量序列如Hibbard经过复杂证明可使得最坏时间复杂度为O(n^(3/2))

流程实例
    // let arr = [8, 7, 1, 5, 6, 4, 2, 3]
    // 上上面数组arr有8个元素,首先设定增量d,数组长度除以2,向下取整。此时d为4
    // 以此划分初始的4个小数组(此处是按照第几个元素,并不是数组真实索引,需要-1)
    // 1+4*0, 1+4*1   1,5   [8,6]
    // 2+4*0, 2+4*1   2,6   [7,4]
    // 3+4*0, 3+4*1   3,7   [1,2]
    // 4+4*0, 4+4*1   4,8   [5,3]
    // 结果为以下数组
    // [8,6],[7,4],[1,2],[5,3]
    // 对每个数组进行直接插入排序
    // [8,6]=>[6,8]  [7,4] =>[4,7]   [1,2] =>[1,2]  [5,3] => [3,5]
    // [6,8] [4,7] [1,2] [3,5]
    // 此时经过第一轮排序(注意索引,)后数组变成[6,4,1,3,8,7,2,5]
    // 增量d = d/2,下一轮d为4/2=2,以2为增量划分数组
    // 1,3,5,7  [6,1,8,2]
    // 2,4,6,8  [4,3,7,5]
    // 直接插入排序
    // [6,1,8,2]=> [1,6,8,2] => [1,6,2,8]=>[1,2,6,8]
    // [4,3,7,5]=[3,4,7,5]=>[3,4,5,7]
    // =>[1,3,2,4,6,5,8,7]
    // =>直接插入排序
    // [1,3,2,4,6,5,8,7]=>[1,2,3,4,6,5,8,7]=>[1,2,3,4,5,6,8,7]=>[1,2,3,4,5,6,7,8]
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
插入排序是一种简单且直观的排序算法。它的基本思想是将一个待排序的数组分成两个部分,一部分是已经排好序的,另一部分是未排序的。在每一次排序过程中,从未排序的部分中取出一个元素插入到已排序的部分中的适当位置,直到所有元素都被插入到已排序的部分中为止。 以下是一个使用微机汇编语言实现的插入排序算法: ```assembly ; 插入排序 ; 参数:edi - 待排序的数组首地址,ecx - 数组元素个数 InsertionSort: mov ebx, 1 ; ebx = 1,从第二个元素开始插入排序 OuterLoop: cmp ebx, ecx ; 检查是否已经排完序 jge Exit ; 如果已经排完序,跳转到 Exit mov eax, [edi + ebx * 4] ; 取出当前待排序的元素 mov edx, ebx ; 将 edx 指向当前待排序元素的前一个元素 InnerLoop: cmp edx, 0 ; 如果已经到了数组的起始位置,跳转到 Insert jl Insert mov esi, [edi + edx * 4] ; 取出当前已排序的元素 cmp esi, eax ; 比较当前已排序的元素和待排序的元素大小 jle Insert ; 如果当前已排序的元素小于等于待排序的元素,跳转到 Insert mov [edi + (edx + 1) * 4], esi ; 将当前已排序的元素后移一位 dec edx ; 将 edx 指向下一个已排序的元素 jmp InnerLoop Insert: mov [edi + (edx + 1) * 4], eax ; 将待排序的元素插入到已排序的部分中 inc ebx ; 将 ebx 指向下一个待排序的元素 jmp OuterLoop Exit: ret ``` 在该算法中,我们使用了寄存器 ebx,eax,edx 和 esi 来存储一些临时变量。其中,ebx 存储当前待排序的元素的下标,eax 存储当前待排序的元素的值,edx 存储当前待排序的元素前面一个已排序的元素的下标,esi 存储当前已排序的元素的值。 该算法的时间复杂度为 O(n^2),其中 n 是数组的元素个数。虽然该算法的效率不高,但是它非常容易实现,并且可以在实际应用中发挥一些作用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值