排序算法基本介绍及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,一经查实,立即删除!

相关文章

二叉搜索树实现

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

快排-荷兰国旗

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

时间空间复杂度概述

找个时间写一写时间复杂度和一些问题分类&#xff0c;也普及一下这方面知识。 如何衡量一个算法好坏 很显然&#xff0c;最重要的两个指标&#xff1a;需要多久可以解决问题、解决问题耗费了多少资源 那我们首先说第一个问题&#xff0c;要多长时间来解决某个问题。那我们可…

二叉树遍历算法总结

文章目录前提要素深度优先搜索DFS经典遍历算法前序遍历递归版迭代版中序遍历递归版迭代版后序遍历递归版迭代版Morris遍历算法中序遍历前序遍历后序遍历广度优先搜索BFS按层遍历参考资料前提要素 本文代码用Java实现。 //二叉树节点结构 public static class TreeNode {publi…

线段树简单实现

首先&#xff0c;线段树是一棵满二叉树。&#xff08;每个节点要么有两个孩子&#xff0c;要么是深度相同的叶子节点&#xff09; 每个节点维护某个区间&#xff0c;根维护所有的。 如图&#xff0c;区间是二分父的区间。 当有n个元素&#xff0c;初始化需要o(n)时间&#xf…

树状数组实现

树状数组能够完成如下操作&#xff1a; 给一个序列a0-an 计算前i项和 对某个值加x 时间o(logn) 注意&#xff1a;有人觉得前缀和就行了&#xff0c;但是你还要维护啊&#xff0c;改变某个值&#xff0c;一个一个改变前缀和就是o(n)了。 线段树树状数组的题就是这样&#x…

KMP子字符串匹配算法学习笔记

文章目录学习资源什么是KMP什么是前缀表为什么一定要用前缀表如何计算前缀表前缀表有什么问题使用next数组来匹配放码过来构造next数组一、初始化二、处理前后缀不相同的情况三、处理前后缀相同的情况使用next数组来做匹配代码总览测试代码时间复杂度分析学习资源 字符串&…

内存分区

之前一直比较懵&#xff0c;想想还是单独写一个短篇来记录吧 一般内存主要分为&#xff1a;代码区、常量区、静态区&#xff08;全局区&#xff09;、堆区、栈区这几个区域。 代码区&#xff1a;存放程序的代码&#xff0c;即CPU执行的机器指令&#xff0c;并且是只读的。 常…

数据结构课上笔记5

介绍了链表和基本操作 用一组物理位置任意的存储单元来存放线性表的数据元素。 这组存储单元既可以是连续的&#xff0c;也可以是不连续的&#xff0c;甚至是零散分布在内存中的任意位置上的。因此&#xff0c;链表中元素的逻辑次序和 物理次序不一定相同。 定义&#xff1a; …

Java设计模式(2 / 23):观察者模式

定义 观察者&#xff08;Observer&#xff09;模式定义了对象之间的一对多依赖&#xff0c;这样一来&#xff0c;当一个对象改变状态时&#xff0c;它的所有依赖者都会收到通知并自动更新。 OO设计原则&#xff1a;为了交互对象之间的松耦合设计而努力。 案例&#xff1a;气…

二叉树概述

各种实现和应用以后放链接 一、二叉树的基本概念 二叉树&#xff1a;二叉树是每个节点最多有两个子树的树结构。 根节点&#xff1a;一棵树最上面的节点称为根节点。 父节点、子节点&#xff1a;如果一个节点下面连接多个节点&#xff0c;那么该节点称为父节点&#xff0c;它…

Java设计模式(1 / 23):策略模式

定义 策略&#xff08;Strategy&#xff09;模式定义了算法族&#xff0c;分别封装起来&#xff0c;让它们之间可以互相替换 &#xff0c;此模式让算法的变化独立于使用算法的客户。 案例&#xff1a;模拟鸭子应用 一开始 新需求&#xff1a;模拟程序需要会飞的鸭子 在父类新…

Java设计模式(3 / 23):装饰者模式

文章目录定义案例1&#xff1a;三点几啦首次尝试再次尝试设计原则&#xff1a;类应该对扩展开放&#xff0c;对修改关闭尝用装饰者模式装饰者模式特征本例的类图放码过来饮料类HouseBlendDarkRoastEspressoDecaf调料装饰类MilkMochaSoyWhip运行测试类案例2&#xff1a;编写自己…

c语言知识体系

原文&#xff1a;https://blog.csdn.net/lf_2016/article/details/80126296#comments

《游戏编程入门 4th》笔记(1 / 14):Windows初步

文章目录Windows编程概述获取Windows理解Windows消息机制多任务多线程事件处理DirectX快速概览Direct3D是什么Window程序基础创建第一个Win32项目理解WinMainWinMain函数调用完整的WinMainGetMessage函数调用寻求帮助Windows编程概述 DirectX&#xff0c;流行的游戏编程库。它…

《游戏编程入门 4th》笔记(2 / 14):监听Windows消息

文章目录编写一个Windows程序理解InitInstanceInitInstance函数调用InitInstance的结构理解MyRegisterClassMyRegisterClass函数调用MyRegisterClass的作用揭露WinProc的秘密WinProc函数调用WinProc的大秘密什么是游戏循环The Old WinMain对持续性的需要实时终止器WinMain和循环…

数据结构课上笔记6

本节课介绍了单链表的操作实现细节&#xff0c;介绍了静态链表。 链表带头的作用&#xff1a;对链表进行操作时&#xff0c;可以对空表、非空表的情况以及 对首元结点进行统一处理&#xff0c;编程更方便。 下面给出带头的单链表实现思路&#xff1a; 按下标查找&#xff1a; …

17校招真题题集(3)11-15

注&#xff1a;本系列题目全是按照通过率降序来排列的&#xff0c;基本保证题目难度递增。 11、 题目名称&#xff1a;买苹果 来源&#xff1a;网易 题目描述 小易去附近的商店买苹果&#xff0c;奸诈的商贩使用了捆绑交易&#xff0c;只提供6个每袋和8个每袋的包装(包装不…

QT5生成.exe文件时,出现缺少QT5core.dll文件解决方法

在 http://qt-project.org/downloads 下载Qt SDK安装需要Qt版本。在QtCreator下&#xff0c;程序可以正常运行&#xff0c;但是当关闭QtCreator后&#xff0c;在DeBug目录下再运行相应的*.exe程序时&#xff0c;会提示缺少Qt5Core.dll错误。解决方法&#xff1a;添加电脑环境变…