排序算法基本介绍及python实现(含详细注释)

对数组排序可以说是编程基础中的基础,本文对八种排序方法做简要介绍并用python实现。

代码中注释很全,适合复习和萌新学习。这是刚入学自己写的,可能难免比不上标准的写法,但是懒得改了。

文末会放和排序相关的基本拓展总结链接。

看不明白可以看群里视频

注意排序实现的具体方式,不要用局部变量,否则占空间太多,和空间复杂度不符。

好,我们开始。

  • 选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在待排序序列的起始位置,直到全部待排序的数据元素排完。时间复杂度O(N^2)

for i in range(len(l)):#意义是第i个位置开始挑第i大(小)的元素for j in range(i,len(l)):#和其他待排序的元素比较if l[j]<l[i]:#更大就交换l[j],l[i]=l[i],l[j]
  • 冒泡排序

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法

它重复地走访过要排序的元素列,一次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换(一般进行n次即可,第n次一定会把第n小的元素放到正确位置)。

这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。时间复杂度O(N^2)

for i in range(len(l)-1):#下标和i无关,代表的只是第i次排序,最多需要len(l)-1次排序即可for j in range(len(l)-1):#遍历每一个元素if l[j]<l[j+1]:#本元素比下一个元素小,就交换l[j],l[j+1]=l[j+1],l[j]

 分析一下其实每次排序都会多一个元素已经确定了位置,不需要再次遍历。

所以j循环可以改成len(l)-i-1

时间复杂度没变。

 

  • 插入排序

有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法——插入排序法,插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。

for i in range(1,len(l)):#意义是第i个元素开始插入i之前的序列(已经有序)for j in range(i,0,-1):#只要比它之前的元素小就交换if l[j]<l[j-1]:l[j],l[j-1]=l[j-1],l[j]else:break#直到比前一个元素大

 

  • 归并排序

速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列

试想:假设已经有两个有序数列,分别存放在两个数组s,r中,我们如何把他们有序的合并在一起?

归并排序就是在重复这样的过程,首先单个元素合并为含有两个元素的数组(有序),然后这种数组再和同类数组合并为四元素数组,以此类推,直到整个数组合并完毕。

def gg(l,ll):#合并函数a,b=0,0k=[]#用来合并的列表while a<len(l) and b<len(ll):#两边都非空if l[a]<ll[b]:k.append(l[a])a=a+1elif l[a]==ll[b]:a=a+1#实现去重else:k.append(ll[b])b=b+1k=k+l[a:]+ll[b:]#加上剩下的return kdef kk(p):#分到只剩一个元素就开始合并if len(p)<=1:return pa=kk(p[0:len(p)//2])#不止一个元素就切片b=kk(p[len(p)//2:])return gg(a,b)#返回排好序的一部分
l=list(map(int,input().split(" ")))
print(kk(l))
  • 快速排序

快速排序(Quicksort)是对冒泡排序的一种改进。

快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

  • 随机化快排

快速排序的最坏情况基于每次划分对主元的选择。基本的快速排序选取第一个元素作为主元。这样在数组已经有序的情况下,每次划分将得到最坏的结果。比如1 2 3 4 5,每次取第一个元素,就退化为了O(N^2)。一种比较常见的优化方法是随机化算法,即随机选取一个元素作为主元。

这种情况下虽然最坏情况仍然是O(n^2),但最坏情况不再依赖于输入数据,而是由于随机函数取值不佳。实际上,随机化快速排序得到理论最坏情况的可能性仅为1/(2^n)。所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望时间复杂度

进一步提升可以分割为三部分,即小于区,等于区,大于区,减小了递归规模,并克服了多元素相同的退化。

def gg(a,b):global lif a>=b:#注意停止条件,我以前没加>卡了半小时returnx,y=a,bimport random#为了避免遇到基本有序序列退化,随机选点g=random.randint(a,b)l[g],l[y]=l[y],l[g]#交换选中元素和末尾元素while a<b:if l[a]>l[y]:#比目标元素大l[a],l[b-1]=l[b-1],l[a]#交换b=b-1#大于区扩大#注意:换过以后a不要加,因为新换过来的元素并没有判断过else:a=a+1#小于区扩大l[y],l[a]=l[a],l[y]#这时a=b#现在解释a和b:a的意义是小于区下一个元素#b是大于区的第一个元素gg(x,a-1)#左右分别递归gg(a+1,y)l=list(map(int,input().split(" ")))
gg(0,len(l)-1)
print(l)
  • 堆排序

堆排序(HeapSort)是一树形选择排序。堆排序的特点是:在排序过程中,将R[l..n]看成是一棵完全二叉树顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(或最小)的记录。

由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。

堆排序是就地排序,辅助空间为O(1).

它是不稳定的排序方法。

主要思想:维持一个大根堆(根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆,又称最大堆。注意:①堆中任一子树亦是堆。②以上讨论的堆实际上是二叉堆(Binary Heap),类似地可定义k叉堆。)

第一步:通过调整原地建立大根堆

第二步:每次交换堆顶和边界元素,并减枝,然后调整堆顶下沉到正确位置。

def down(i,k):#在表l里的第i元素调整,k为边界#优先队列也是通过这种方式实现的global lwhile 2*i+2<k:#右孩子不越界lift,right=2*i+1,2*i+2m=max(l[i],l[lift],l[right])if m==l[i]:#不需要调breakif m==l[lift]:#把最大的换上来l[i],l[lift]=l[lift],l[i]i=lift#目的节点下标更新else:#把最大的换上来l[i],l[right]=l[right],l[i]i=right#目的节点下标更新if 2*i+1<k:#判断左孩子if l[2*i+1]>l[i]:l[i],l[2*i+1]=l[2*i+1],l[i]def main():global lfor j in range(1,len(l)+1):#调大根堆i=len(l)-jdown(i,len(l))for i in range(len(l)-1,-1,-1):#排序l[i],l[0]=l[0],l[i]#最大和边界交换,剪枝down(0,i)print(l)l=list(map(int,input().split(" ")))
main()
  • 桶排序

桶排序不是基于比较的排序方法,只需对号入座。将相应的数字放进相应编号的桶即可。

当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间o(n)

对于海量有范围数据十分适合,比如全国高考成绩排序,公司年龄排序等等。

l=list(map(int,input().split(" ")))
n=max(l)-min(l)
p=[0]*(n+1)#为了省空间
for i in l:p[i-min(l)]=1#去重排序,做标记即可
for i in range(n):if p[i]==1:#判断是否出现过print(i+min(l),end=" ")
  • 希尔排序

希尔排序(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因D.L.Shell于1959年提出而得名。

通过缩小有序步长来实现。

 

def shell(arr):n=len(arr)#初始化步长h=1while h<n/3:h=3*h+1while h>=1:#判断,退出后就有序了。for i in range(h,n):j=iwhile j>=h and arr[j]<arr[j-h]:#判断是否交换arr[j], arr[j-h] = arr[j-h], arr[j]j-=hh=h//3#逐渐缩小步长print arr

稳定性及时间复杂度

排序稳定性概念:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

时间复杂度:时间复杂度是同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适算法和改进算法。可以理解为和常数操作所成的一种关系(常数操作为O(1))

空间复杂度类似。

下面给出各类排序的对比图:

 

 

 

  • 基数排序

因为桶排序是稳定的,基数排序就是很多次桶排序而已,按位进行桶排序即可。

(个人认为桶排序名字不恰当,因为桶是先进后出,和稳定的算法正好反了,)

 

 

 

 

总:

比较排序和非比较排序

      常见的排序算法都是比较排序,非比较排序包括计数排序、桶排序和基数排序,非比较排序对数据有要求,因为数据本身包含了定位特征,所有才能不通过比较来确定元素的位置。

      比较排序的时间复杂度通常为O(n2)或者O(nlogn),比较排序的时间复杂度下界就是O(nlogn),而非比较排序的时间复杂度可以达到O(n),但是都需要额外的空间开销。

  • 若n较小(数据规模较小),插入排序或选择排序较好
  • 若数据初始状态基本有序(正序),插入、冒泡或随机快速排序为宜
  • 若n较大,则采用时间复杂度为O(nlogn)的排序方法:快速排序或堆排序
  • 快速排序是目前基于比较的排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
  • 堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。

 

 

各种拓展以后放链接

 

 归并求逆序:https://blog.csdn.net/hebtu666/article/details/81773190

快排前k小:https://blog.csdn.net/hebtu666/article/details/81772978这个bfprt算法以后再写,也不难。

快排荷兰国旗:https://blog.csdn.net/hebtu666/article/details/81772701

堆:https://blog.csdn.net/hebtu666/article/details/81706288

 

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/445655.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第二次作业 讲解及展示

第二次作业&#xff0c;同学们虽然在认真完成&#xff0c;但是或多或少都出了一些错误&#xff0c;一班张婷&#xff0c;四班武仪人&#xff0c;六班杨泽宇&#xff0c;八班候雯洁&#xff0c;安锦阳&#xff0c;刘净圆&#xff0c;这些同学完成的较为出色&#xff0c;错误较少…

深搜DFS\广搜BFS 图初步入门

首先&#xff0c;不管是BFS还是DFS&#xff0c;由于时间和空间的局限性&#xff0c;它们只能解决数据量比较小的问题。 深搜&#xff0c;顾名思义&#xff0c;它从某个状态开始&#xff0c;不断的转移状态&#xff0c;直到无法转移&#xff0c;然后退回到上一步的状态&#xf…

素数基本(埃氏筛法/线性筛法)

一、检查n是否为素数 最简单思路&#xff1a;所有可能的因数全部试一遍。 int gg(int n) {for(int i2;i<n;i){if((n%i)0)return 0;//有因数就不是素数咯}return 1; } 进一步思考&#xff1a;没必要枚举所有的数&#xff0c;每一个小于n^(1/2)的因数i&#xff0c;一定有一个大…

欧几里得gcd/extend_gcd

正式叙述前还写了一点自己的小感受。 问题&#xff1a;求两个正整数a&#xff0c;b的最大公约数。 大神看来是很简单的问题&#xff0c;但是对于去年夏天刚学python的我来说&#xff0c;这是个很难的问题&#xff0c;还记得当时一晚上睡不着都在想怎么快一点的求出最大公约数…

python基础技巧总结(一)

最近总结一下python基础知识&#xff0c;就暂时弃坑了。 本文总结只属于python的一些骚操作。。。 后面文章自行去博客学习交流 原地交换 Python 提供了一个直观的在一行代码中赋值与交换&#xff08;变量值&#xff09;的方法 x, y 10, 20 print(x, y)x, y y, x print(x…

python基础技巧总结(二)

一总结的链接&#xff1a; 好&#xff0c;我们继续 一次性初始化多个变量 可以直接赋值&#xff1a; a,b,c,d1,2,3,4 可以利用列表&#xff1a; List [1,2,3] x,y,zList print(x, y, z) #-> 1 2 3 &#xff08;元素个数应与列表长度相同&#xff09; 打印模块路径 im…

python基础技巧总结(三)

前两篇文章&#xff1a; https://blog.csdn.net/hebtu666/article/details/81698235 https://blog.csdn.net/hebtu666/article/details/81698329 我们继续总结&#xff1a; 开启文件分享 Python 允许运行一个 HTTP 服务器来从根路径共享文件&#xff0c;下面是开启服务器的…

python基础技巧总结(四)

前三期请到我博客里找 https://blog.csdn.net/hebtu666 我们继续总结 except的用法和作用 try/except: 捕捉由PYTHON自身或写程序过程中引发的异常并恢复 except: 捕捉所有其他异常 except name: 只捕捉特定的异常 except name, value: 捕捉异常及格外的数据(实例) exce…

python基础技巧总结(五)

前四期到博客找&#xff1a;https://blog.csdn.net/hebtu666 我们继续说一些好用的函数 split Python split() 通过指定分隔符对字符串进行切片&#xff0c;如果参数 num 有指定值&#xff0c;则仅分隔 num 个子字符串。 语法&#xff1a; str.split(str"", num…

堆的简单实现

关于堆不做过多介绍 堆就是儿子的值一定不小于父亲的值并且树的节点都是按照从上到下&#xff0c;从左到右紧凑排列的树。 &#xff08;本文为二叉堆&#xff09; 具体实现并不需要指针二叉树&#xff0c;用数组储存并且利用公式找到父子即可。 父&#xff1a;(i-1)/2 子:…

二叉搜索树实现

本文给出二叉搜索树介绍和实现 首先说它的性质&#xff1a;所有的节点都满足&#xff0c;左子树上所有的节点都比自己小&#xff0c;右边的都比自己大。 那这个结构有什么有用呢&#xff1f; 首先可以快速二分查找。还可以中序遍历得到升序序列&#xff0c;等等。。。 基本操…

python基础小白题

题目1&#xff1a;有1、2、3、4四个数&#xff0c;能组成多少个互不相同且无重复的三位数&#xff1f;都是多少&#xff1f; list_num[1,2,3,4] all_num[] for i in list_num: for j in list_num: for k in list_num : if (i!j) and (i!k) and (j!k): numi*100j*10k all_num…

python基础小白题2

题目11&#xff1a;判断101-200之间有多少个素数&#xff0c;并输出所有素数。 num[] for i in range(100,201): ji//2 for k in range(2,j): if i%k0: break else: num.append(i) print(一共有%d个素数\n这些素数是&#xff1a; %len(num),num ) 输出结果&am…

python基础小白题3

题目021&#xff1a;猴子吃桃问题 猴子第一天摘下若干个桃子&#xff0c;当即吃了一半&#xff0c;还不瘾&#xff0c;又多吃了一个 第二天早上又将剩下的桃子吃掉一半&#xff0c;又多吃了一个。 以后每天早上都吃了前一天剩下的一半零一个。 到第10天早上想再吃时&#x…

python基础小白题4

题目031&#xff1a;请输入星期几的第一个字母来判断一下是星期几&#xff0c;如果第一个字母一样&#xff0c;则继续判断第二个字母。 def tm031(): 【个人备注】&#xff1a;按照题意要求实现了就行 week [monday,tuesday,wednesday,thursday,friday,saturday,sunday] inp…

python基础小白题5

题目045&#xff1a;统计 1 到 100 之和。 def tm045(): 【个人备注】&#xff1a;简单&#xff0c;但官网有人写的更简单 s 0 for i in range(1,101): si print(s) # 更简洁的方法 print(sum(range(1,101))) 题目046&#xff1a;求输入数字的平方&#xff0c;如果平方运算…

快排-荷兰国旗

在使用partition-exchange排序算法时&#xff0c;如快速排序算法&#xff0c;我们会遇到一些问题&#xff0c;比如重复元素太多&#xff0c;降低了效率&#xff0c;在每次递归中&#xff0c;左边部分是空的(没有元素比关键元素小)&#xff0c;而右边部分只能一个一个递减移动。…

快排-前m大元素

描述 给定一个数组包含n个元素&#xff0c;统计前m大的数并且把这m个数从大到小输 出。 输入 第一行包含一个整数n&#xff0c;表示数组的大小。n < 100000。 第二行包含n个整数&#xff0c;表示数组的元素&#xff0c;整数之间以一个空格分开 。每个整数的绝对值不超过…

归并-求逆序数

考虑1,2,…,n (n < 100000)的排列i1&#xff0c;i2&#xff0c;…&#xff0c;in&#xff0c;如果其中存在j,k&#xff0c;满足 j < k 且 ij > ik&#xff0c; 那么就称(ij,ik)是这个排列的一个逆序。 一个排列含有逆序的个数称为这个排列的逆序数。例如排列 26345…

动态规划概述

注&#xff1a;第一次看不需要全理解&#xff0c;以后动态规划做多了&#xff0c;再回来看看&#xff0c;会有更深的理解 先符上其它文章&#xff0c;看完这篇就可以开始看这些咯。 萌新&#xff1a; …