排序算法系列1--简单排序(选择,冒泡,直接插入,希尔排序)

2023-10-29

排序是数据处理中十分常见的操作,现代高级语言都有现成的n种排序算法。但了解它们的代码,对计算机思维有帮助。

 

简单选择排序

每一趟从待排序的数据元素中选择最小(或最大)的一个元素作为首元素,直到所有元素排完为止,简单选择排序是不稳定排序。

无论数组原始排列如何,比较次数都不变;变的是交换次数。完全有序的情况下无需交换移动元素,最差情况下(把数组倒序改成正序),交换次数最多: n-1。

时间复杂度是n2

 

冒泡排序 

以前的博文:https://www.cnblogs.com/chentianwei/p/8244728.html

比较相邻的两个元素,如果他们的顺序错误就把他们交换过来。

n个数,进行n-1轮比较。 每轮归位1个最大/最小数,已经归位的数下一轮无需再比较。

冒泡的比喻就是:每轮把最大/最小值放到数组的最后。好像冒气泡。

时间复杂度是n2

 

直接插入排序

直接插入排序基本思想是每一步将一个待排序的记录,插入到前面已经排好序的有序序列中去,直到插完所有元素为止。

简单插入排序在最好情况下,需要比较n-1次,无需交换元素,时间复杂度为O(n);在最坏情况下,时间复杂度依然为O(n2)。但是在数组元素随机排列的情况下,插入排序还是要优于上面两种排序的。

时间复杂度依然为n2

 


 

希尔排序 

git代码

也称为:in-place comparison sort。

⚠️in place algorithm即原地算法:基本不需要额外辅助的数据结构,可能需要少量额外的辅助变量来转换数据的算法。

希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。 

⚠️递减增量: 每轮逐步减少步长。步长是“将要比较的2个元素”中间间隔的其他元素的数量。

 

算法的实现:

这个算法是计算机早期的一种算法,比冒泡,插入速度更快。这是因为这个算法的元素比较,不是相邻的连个元素比较,而是一个元素和距离它较远的元素进行比较。即用较大的步子长度来降低比较的次数,这样速度就快了很多。

 

举例:

数组a, 有12个元素。使用步长5,3,1。如图。

 

 

 

 
  

第一次以步长等于5分组。结果可见:

17, 28,18,47,  07

25,83,86,53, 69

62,95

第一列到第5列都是从小到大的正序排列了。

 

理论的sort方式:
  1. 按照元素之间的gap分组。“对分组的元素进行排序”。(在数组原位置进行比较和交换,即in-place)。
  2. ⚠️,关于第一步的具体方法见代码说明。
  3. 减少元素之间的gap。这样分到一组的元素增加了。还是按照第一步操作。
  4. 递减gap操作,即每次都减少元素之间的gap,直到没有距离, 即所有的元素都分在一组。排序后,就是一个有序数组。

关于第二步骤:

每组元素都需要进行排序操作,如何做到? 使用插入排序法:

首先, 对分到同组元素,前两个元素比较和交换,成为有序数组。

然后,第3个元素和第2,1个元素比较并插入到合适的位置。

再后,如同上一步,第4个元素,和第3,2,1个元素比较并插入。

最后,当最后一个元素被插入到合适位置后,本组元素排序完成。

 

因为原数组a是无序的,并使用gap_sort。所以当gap = 3则,设置i =3。

要比较数组的所有元素,所以遍历从i到length -1的所有元素,每个元素都用插入排序法。因为0到i-1的元素和i到i +gap比较,所以无需遍历:

    i = gap
    while i < arr.length
      temp = arr[i]
      # 插入排序
      #...
      i += 1
    end

深入i循环内部,每个i的插入排序:

    while i < arr.length
      temp = arr[i]
      j = i
      while j >= gap && arr[j - gap] > temp
        arr[j] = arr[j-gap]
        j -= gap
      end
      arr[j] = temp
      i += 1
    end

 

arr[j]和它同组的前面的元素arr[j - gap], 比较大小。

变量j的第1..(1+ gap)次循环内部:每次只有2个元素比较,相当于第2个元素插入到第1个元素的前面或后面。形成只有2个元素的有序数列。

上面的插入代码不太好理解, 其实就是插入排序法。

可以这么想或理解:

  • 把gap假设是数字1,即同组元素相邻
  • 把待插元素放到数组尾部,从尾部往头部的方向,逐个和待插值比较。

具体代码见下面。

 

总结:

步长的选择是希尔排序的最关键的部分。

算法开始以一定的步长进行排序。然后会逐步减少步长进行排序,最终算法以步长为1进行排序。当步长为1时,算法变为普通插入排序。

Donald Shell最初建议步长选择n/2,然后每轮对上轮的步长取半, 直到步长达到1。即{n/2,(n/2)/2...1}。

步长的选择,和数列的特性(大数据,带小数等)决定了整体的时间复杂度。所以这个排序法是不稳定的。

当步长是n/2i,最坏情况下仍然是O(n2)。

 

Ruby代码:(使用步长 n/2i),便于理解的代码:
def shell_sort(arr)
  gap = arr.length/2
  # 使用的是n除以2的i次方的步距。最后一轮gap等于1.
  while gap > 0
    i = gap
    # 遍历从i开始的元素, i前面的元素无需遍历。因为插入排序法,从后往前比较。
    while i < arr.length
      # 设置指针j, 指针是要前移的。
      # 相当于在队尾插入一个新元素,然后和前面的同组相邻元素比较和交换位置。
      # 如此反复直到该元素找到确定位置。
      j = i
      while j >= gap && arr[j - gap] > arr[j]
        # 被分到同组的相邻元素交换位置
        temp = arr[j]
        arr[j] = arr[j - gap]
        arr[j - gap ] = temp
        # 指针前移一个位置
        # 此时,arr[j]位置的值是插入的元素,它会在下轮循环和前面的元素比较。
        j = j - gap
      end
      i += 1
    end
    gap = gap/2
  end
  return arr
end

p b = (1..50).to_a.shuffle

p shell_sort(b)

 

 

上面对被分组的元素使用的排序法,不是插入排序,而是类似冒泡排序,即每次比较相邻两个元素并交换值value。

有更节省时间的改进代码:

无需每次都交换值。把待插入元素和前面的同组元素一一比较,只移动大于该元素的元素的value,最后再插入这个元素的value即可。这样节省了很多时间。

def shell_sort(arr)
  gap = arr.length / 2

  while gap > 0
    i = gap
    # 遍历从i开始的元素, i前面的元素无需遍历。因为插入排序法,从后往前。
    while i <= arr.length - 1
      temp = arr[i]
      j = i
      while j >= gap && arr[j - gap] > temp
        arr[j] = arr[j - gap]
        # arr[j - gap] = temp
        j = j - gap
      end
      # 最后插入值。
      arr[j] = temp
      i += 1
    end
    gap = gap/2
  end
  return arr
end

p b = (1..12).to_a.shuffle
p shell_sort(b)

 

 

https://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F 

参考:https://www.cnblogs.com/chengxiao/p/6103002.html

转载于:https://www.cnblogs.com/chentianwei/p/11620637.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

排序算法系列1--简单排序(选择,冒泡,直接插入,希尔排序) 的相关文章

  • Mongodb shell mongo:通常只允许每个套接字地址(协议/网络地址/端口)使用一次。对于套接字:0.0.0.0:27017

    前两天还好好的 现在mongod不起作用 甚至echo ps A grep mongo没有打印任何内容 但它警告错误 每个套接字地址只有一种用途 怎么杀掉它 我也尝试过不同的随机端口 他们怎么可能也失败呢 D mongodb win32 x
  • shell脚本中的\r字符

    我在尝试执行 shell 脚本时收到以下错误 r command not found line 2 请提出同样的解决方案 以下是脚本中使用的初始行 bin sh if lt 1 then echo ERROR Environment arg
  • 如何执行“sudo nvm”?

    在我的 Mac 上 我想将一些需要 su 权限的包迁移到另一个节点版本 我使用 homebrew 安装 nvm 现在我需要执行 sudo nvm 或 reinstall packages将失败 me MacBook sudo nvm sud
  • C程序调用shell脚本

    我有一个小型 C 程序 调用 shell 脚本 myScript sh 我得到的 ret 值为 256 请帮助我了解系统调用出了什么问题 int main int ret ret system myScript sh ret gt gt r
  • 如何在 Windows 下向 .sh 脚本传递参数?

    我正在尝试在 Windows 下执行 sh 脚本 我安装了 Git 它允许我执行 sh 文件 但是 如果不使用 sh 作为执行前缀 我似乎无法传递任何参数 我的 sh 文件 echo Test 1 如果我用以下命令执行它 gt sh tes
  • 使用 sh 运行 bash 脚本

    我有 bash 脚本 它需要 bash 另一个人尝试运行它 sh script name sh 它失败了 因为 sh 是他的发行版中 dash 的符号链接 ls la bin sh lrwxrwxrwx 1 root root 4 Aug
  • 使用正则表达式模式查找 -name 并使用 cp 替换文件名

    目前我正在使用该命令cron复制 data从源到目标路径 find source path name data exec cp target path 源码结构为 source path category1 001 data source
  • 如何从我自己的脚本向 Fish shell 提供制表符补全?

    我运行的是 Ubuntu 13 10 和 Fish 2 1 0 我想自己编写一个 Python 脚本来从命令行执行一些任务 该脚本将需要命令行参数 我怎样才能编写我的脚本 以便 Fish 可以请求并获取给定参数的可能值 潜在值列表是动态的
  • 通过 sed 使用 unix 变量将数据附加到每行末尾[重复]

    这个问题在这里已经有答案了 我有一个文件 我想使用 SED 将值附加到每行末尾的 unix 变量中 我已经通过 AWK 实现了这一点 但我想在 SED 中实现 像这样的东西 我已经尝试过以下命令 但它不起作用 sed i s BATCH R
  • sh / Bash shell 脚本中 !# (bang-pound) 的含义是什么?

    我想了解这个 Scala 脚本是如何工作的 usr bin env bash exec scala 0 object HelloWorld def main args Array String println Hello world arg
  • Shell 脚本中的块注释

    有没有一种简单的方法可以注释掉 shell 脚本中的代码块 In bash bin bash echo before comment lt lt END bla bla blurfl END echo after comment The a
  • Bash 脚本监听按键以继续

    因此 我想编写一个由一系列步骤组成的 bash 脚本 并将其标识为 task 然而 每个步骤都只能完成并且可以根据用户的需要运行 Do task1 if keypressed stop task1 and move on this is t
  • 这种 bash 文件名提取技术有何用途?

    我有一部分 bash 脚本正在获取不带扩展名的文件名 但我试图了解这里到底发生了什么 是做什么用的 有人可以详细说明 bash 在幕后做了什么吗 如何在一般基础上使用该技术 bin bash for src in tif do txt sr
  • 如果未设置,则从控制台读取 Makefile 变量

    我正在更新一个从外部源访问某些资源的 Makefile 即存在以下形式的规则 External cvs up 对于不受限制的资源 它可以按预期工作 现在 出现了功能漂移 外部资源需要更复杂的登录 因此规则已更改为与此没有太大不同的内容 Ex
  • 无需 root 访问权限即可安装 zsh? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 有可能 以及如何 我确实需要在几台具有 ssh 访问权限 但没有 root 访问权限 的远程计算机上使用此功能 下载 zsh wget O zsh t
  • awk 返回两个变量

    现在这就是我正在做的事情 ret ls la awk print 3 9 usr echo ret awk print 1 fil echo ret awk print 2 问题是我没有运行ls我正在运行一个需要时间的命令 因此您可以理解其
  • exec()、shell_exec()、curl_exec() 的安全漏洞

    有时 我会使用 exec shell exec 和curl exec 以下是典型用途 假设其中有 PHP 变量 即第一个变量中的 html 用户有可能修改其内容 从安全漏洞的角度来看 我应该关注什么 escapeshellcmd 和 esc
  • PHP exec rm -Rf 不适用于子目录

    我试图删除特定文件夹中的所有内容 但它似乎不会影响子文件夹 但它应该 因为 bash 命令是从控制台执行的 system rm Rf some dir 该命令中不需要星号 如果要与文件一起删除目录 请同时删除斜杠 留下斜杠将删除文件 但保留
  • 在 bash 中,如何除以两个变量并输出四舍五入到小数点后 5 位的答案? [复制]

    这个问题在这里已经有答案了 我将两个变量作为输入 将它们相除后 我希望将输出四舍五入到小数点后 5 位 我已经尝试过这种方法 gt sum 12 n 7 output scale 5 sum n bc echo output 我的代码没有显
  • 查找并删除超过 x 天的文件或文件夹

    我想删除超过 7 天的文件和文件夹 所以我尝试了 17 07 14 email protected cdn cgi l email protection find tmp mindepth 1 maxdepth 1 ctime 7 exec

随机推荐