用Python手写五大经典排序算法,看完这篇终于懂了!
Posted shannian999
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用Python手写五大经典排序算法,看完这篇终于懂了!相关的知识,希望对你有一定的参考价值。
算法作为程序员的必修课,是每位程序员必须掌握的基础。作为Python忠实爱好者,本篇东哥将通过Python来手撕5大经典排序算法,结合例图剖析内部实现逻辑,对比每种算法各自的优缺点和应用点。相信我,耐心看完绝对有收获。
很多人学习python,不知道从何学起。
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!
QQ群:1097524789
前戏准备
大家都知道从理论上讲,我们一般会使用大O表示法测量算法的运行时复杂度。"大O表示法"表示程序的执行时间或占用空间随数据规模的增长趋势。
但为了测算具体的时间,本篇将使用timeit
模块来衡量实现的运行时间。下面自己写一个对算法测试时间的函数。
这里用到了一个骚操作,通过f-strings魔术方法导入了算法名称,不懂的可以自行查看使用方法。
注意:应该找到算法每次运行的平均时间,而不是选择单个最短时间。由于系统同时运行其他进程,因此时间测量是受影响的。最短的时间肯定是影响最小的,是这样才使其成为算法时间最短的。
Python中的冒泡排序算法
冒泡排序是最直接的排序算法之一。它的名称来自算法的工作方式:每经过一次新的遍历,列表中最大的元素就会“冒泡”至正确位置。
在Python中实现冒泡排序
这是Python中冒泡排序算法的实现:
为了正确分析算法的工作原理,看下这个列表[8, 2, 6, 4, 5]
。假设使用bubble_sort()
排序,下图说明了算法每次迭代时数组的实际换件情况:
冒泡排序过程
测算冒泡算法的大O运行复杂度
冒泡排序的实现由两个嵌套for
循环组成,其中算法先执行n-1个比较,然后进行n-2个比较,依此类推,直到完成最终比较。因此,总的比较次数为(N - 1)+(N - 2)+(N - 3)+ ... + 2 + 1 = N(N-1)/ 2,也可以写成 ½n 2 - ½n。
去掉不会随数据规模n而变化的常量,可以将符号简化为 n2 -n。由于 n2的增长速度快于n,因此也可以舍弃最后一项,使冒泡排序的平均和最坏情况下的时间复杂度为 O(n 2)。
在算法接收到已排序的数组的情况下,运行时间复杂度将降低到更好的O(n),因为算法循环一遍没有任何交换,标志是true
,所以循环一遍比较了N
次直接退出。因此,O(n)是冒泡排序的最佳情况运行时间复杂度。但最好的情况是个例外,比较不同的算法时,应该关注平均情况。
冒泡排序的时间运行测试
使用run_sorting_algorithm()
测试冒泡排序处理具有一万个元素的数组所花费的时间。
ARRAY_LENGTH = 10000
if __name__ == "__main__": # 生成包含“ ARRAY_LENGTH”个元素的数组,元素是介于0到999之间的随机整数值 array = [randint(0, 1000) for i in range(ARRAY_LENGTH)] # 使用排序算法的名称和刚创建的数组调用该函数 run_sorting_algorithm(algorithm="bubble_sort", array=array)
现在运行脚本来获取bubble_sort
的执行时间:
分析冒泡排序的优缺点
冒泡排序算法的主要优点是它的简单性,理解起来非常简单。但也看到了冒泡排序的缺点是速度慢,运行时间复杂度为O(n 2)。因此,一般对大型数组进行排序的时候,不会考虑使用冒泡排序。
Python中的插入排序算法
像冒泡排序一样,插入排序算法也易于实现和理解。但是与冒泡排序不同,它通过将每个元素与列表的其余元素进行比较并将其插入正确的位置,来一次构建一个排序的列表元素。此“插入”过程为算法命名。
一个例子,就是对一副纸牌进行排序。将一张卡片与其余卡片进行逐个比较,直到找到正确的位置为止,然后重复进行直到您手中的所有卡都被排序。
在Python中实现插入排序
插入排序算法的工作原理与纸牌排序完全相同,Python中的实现:
下图显示了对数组进行排序时算法的不同迭代[8, 2, 6, 4, 5]
:
插入排序过程
测量插入排序的大O时间复杂度
与冒泡排序实现类似,插入排序算法具有两个嵌套循环,遍历整个列表。内部循环非常有效,因为它会遍历列表,直到找到元素的正确位置为止。
最坏的情况发生在所提供的数组以相反顺序排序时。在这种情况下,内部循环必须执行每个比较,以将每个元素放置在正确的位置。这仍然给您带来O(n2)运行时复杂性。
最好的情况是对提供的数组进行了排序。这里,内部循环永远不会执行,导致运行时复杂度为O(n),就像冒泡排序的最佳情况一样。
尽管冒泡排序和插入排序具有相同的大O时间复杂度,但实际上,插入排序比冒泡排序有效得多。如果查看两种算法的实现,就会看到插入排序是如何减少了对列表进行排序的比较次数的。
插入排序时间测算
为了证明插入排序比冒泡排序更有效,可以对插入排序算法进行计时,并将其与冒泡排序的结果进行比较。调用我们写好的测试函数。
执行脚本:
可以看到,插入排序比冒泡排序实现少了17
秒,即使它们都是O(n 2)算法,插入排序也更加有效。
分析插入排序的优点和缺点
就像冒泡排序一样,插入排序算法的实现也很简单。尽管插入排序是O(n 2)算法,但在实践中它也比其他二次实现(例如冒泡排序)更有效。
有更强大的算法,包括合并排序和快速排序,但是这些实现是递归的,在处理小型列表时通常无法击败插入排序。如果列表足够小,可以提供更快的整体实现,则某些快速排序实现甚至在内部使用插入排序。Timsort还在内部使用插入排序对输入数组的一小部分进行排序。
也就是说,插入排序不适用于大型阵列,这为可以更有效地扩展规模的算法打开了大门。
Python中的合并排序算法
合并排序是一种非常有效的排序算法。它基于分治法,这是一种用于解决复杂问题的强大算法技术。
要正确理解分而治之,应该首先了解递归的概念。递归涉及将问题分解成较小的子问题,直到它们足够小以至于无法解决。在编程中,递归通常由调用自身的函数表示。
分而治之算法通常遵循相同的结构:
原始输入分为几个部分,每个部分代表一个子问题,该子问题与原始输入相似,但更为简单。每个子问题都递归解决。所有子问题的解决方案都组合成一个整体解决方案。在合并排序的情况下,分而治之方法将输入值的集合划分为两个大小相等的部分,对每个一半进行递归排序,最后将这两个排序的部分合并为一个排序列表。
在Python中实现合并排序
合并排序算法的实现需要两个不同的部分:
递归地将输入分成两半的函数 合并两个半部的函数,产生一个排序数组 这是合并两个不同数组的代码:
现在还缺少的部分是将输入数组递归拆分为两个并用于merge()
产生最终结果的函数:
看一下合并排序将对数组进行排序的步骤[8, 2, 6, 4, 5]
:
合并排序过程
该图使用黄色箭头表示在每个递归级别将数组减半。绿色箭头表示将每个子阵列合并在一起。
衡量合并排序的大O复杂度
要分析合并排序的复杂性,可以分别查看其两个步骤:
merge()
具有线性运行时间。它接收两个数组,它们的组合长度最多为n(原始输入数组的长度),并且通过最多查看每个元素一次来组合两个数组。这导致运行时复杂度为O(n)。
第二步以递归方式拆分输入数组,并调用merge()
每一部分。由于将数组减半直到剩下单个元素,因此此功能执行的减半运算总数为log 2 n。由于merge()
每个部分都被调用,因此总运行时间为O(n log 2 n)。
对合并排序进行测算时间
同样通过之前时间测试函数:
执行时间:
与冒泡排序和插入排序相比,合并排序实现非常快,可以在一秒钟内对一万个数组进行排序了!
分析合并排序的优点和缺点
由于其运行时复杂度为O(n log 2 n),因此合并排序是一种非常有效的算法,可以随着输入数组大小的增长而很好地扩展。并行化也很简单,因为它将输入数组分成多个块,必要时可以并行分配和处理这些块。
缺点是对于较小的列表,递归的时间成本就较高了,冒泡排序和插入排序之类的算法更快。例如,运行包含十个元素的列表的实验的时间:
合并排序的另一个缺点是,它在递归调用自身时会创建数组。它还在内部创建一个新列表,这使得合并排序比气泡排序和插入排序使用更多的内存。
Python中的快速排序算法
就像合并排序一样,快速排序算法采用分而治之的原理将输入数组分为两个列表,第一个包含小项目,第二个包含大项目。然后,该算法将对两个列表进行递归排序,直到对结果列表进行完全排序为止。
划分输入列表称为对列表进行分区。快排首先选择一个pivot
元素,然后将列表划分为pivot,然后将每个较小的元素放入low数组
,将每个较大的元素放入high数组
。
将low列表中的每个元素放在列表的左侧,列表中的pivot每个元素high放在右侧,将其pivot精确定位在最终排序列表中的确切位置。这意味着该函数现在可以递归地将相同的过程应用于low,然后high对整个列表进行排序。
在Python中实现快排
这是快排的一个相当紧凑的实现:
以下是快排对数组进行排序的步骤的说明[8, 2, 6, 4, 5]
:
快排排序过程
快速排序流程 黄线表示阵列的划分成三个列表:low,same,high
。绿线表示排序并将这些列表放在一起。
选择pivot元素
为什么上面的实现会pivot随机选择元素?选择输入列表的第一个或最后一个元素会不会一样?
由于快速排序算法的工作原理,递归级别的数量取决于pivot每个分区的结尾位置。在最佳情况下,算法始终选择中值元素作为pivot。这将使每个生成的子问题恰好是前一个问题的一半,从而导致最多log 2 n级。
另一方面,如果算法始终选择数组的最小或最大元素作为pivot,则生成的分区将尽可能不相等,从而导致n-1个递归级别。对于快速排序,那将是最坏的情况。
如你所见,快排的效率通常取决于pivot选择。如果输入数组未排序,则将第一个或最后一个元素用作,pivot将与随机元素相同。但是,如果输入数组已排序或几乎已排序,则使用第一个或最后一个元素作为pivot可能导致最坏的情况。pivot随机选择使其更有可能使快排选择一个接近中位数的值并更快地完成。
另一个选择是找到数组的中值,并强制算法将其用作pivot。这可以在O(n)时间内完成。尽管该过程稍微复杂一些,但将中值用作pivot快速排序可以确保您拥有最折中的大O方案。
衡量快排的大O复杂度
使用快排,将输入列表按线性时间O(n)进行分区,并且此过程将平均递归地重复log 2 n次。这导致最终复杂度为O(n log 2 n)。
当所选择的pivot
是接近阵列的中位数,最好的情况下会发生O(n),当pivot
是阵列的最小或最大的值,最差的时间复杂度为O(n 2)。
从理论上讲,如果算法首先专注于找到中值,然后将其用作pivot元素,那么最坏情况下的复杂度将降至O(n log 2 n)。数组的中位数可以在线性时间内找到,并将其用作pivot
保证代码的快速排序部分将在O(n log 2 n)中执行。
通过使用中值作为pivot
,最终运行时间为O(n)+ O(n log 2 n)。你可以将其简化为O(n log 2 n),因为对数部分的增长快于线性部分。
随机选择pivot使最坏情况发生的可能性很小。这使得随机pivot选择对于该算法的大多数实现都足够好。
对快排测量运行时间
调用测试函数:
执行脚本:
快速排序不仅可以在不到一秒钟的时间内完成,而且比合并排序(0.11几秒钟对0.61几秒钟)要快得多。如果增加ARRAY_LENGTH
从10,000
到数量1,000,000
并再次运行脚本,合并排序最终会在97几秒钟内完成,而快排则仅在10几秒钟内对列表进行排序。
分析快排的优势和劣势
顾名思义,快排非常快。尽管从理论上讲,它的最坏情况是O(n 2),但在实践中,快速排序的良好实现胜过大多数其他排序实现。而且,就像合并排序一样,快排也很容易并行化。
快排的主要缺点之一是缺乏保证达到平均运行时复杂度的保证。尽管最坏的情况很少见,但是某些应用程序不能承受性能不佳的风险,因此无论输入如何,它们都选择不超过O(n log 2 n)的算法。
就像合并排序一样,快排也会在内存空间与速度之间进行权衡。这可能成为对较大列表进行排序的限制。
通过快速实验对十个元素的列表进行排序,可以得出以下结果:
结果表明,当列表足够小时,快速排序也要付出递归的代价,完成时间比插入排序和冒泡排序都要长。
Python中的Timsort算法
最后这个算法就有意思了!
所述Timsort算法被认为是一种混合的排序算法,因为它采用插入排序和合并排序的最佳的两个世界级组合。Timsort与Python社区也很有缘,它是由Tim Peters于2002年创建的,被用作Python语言的标准排序算法。我们使用的内置sorted函数就是这个算法。
Timsort的主要特征是它利用了大多数现实数据集中存在的已排序元素。这些称为natural runs。然后,该算法会遍历列表,将元素收集到运行中,然后将它们合并到一个排序的列表中。
在Python中实现Timsort
本篇创建一个准系统的Python实现,该实现说明Timsort算法的所有部分。如果有兴趣,也可以查看Timsort的原始C实现。
实施Timsort的第一步是修改insertion_sort()
的实现:
此修改后的实现添加了两个参数left和right,它们指示应该对数组的哪一部分进行排序。这使Timsort算法可以对数组的一部分进行排序。修改功能而不是创建新功能意味着可以将其同时用于插入排序和Timsort。
现在看一下Timsort的实现:
尽管实现比以前的算法要复杂一些,但是我们可以通过以下方式快速总结一下:
之前已经了解到,插入排序在小列表上速度很快,而Timsort则利用了这一优势。Timsort使用新引入的left
和right
参数在insertion_sort()
对列表进行适当排序,而不必像merge sort和快排那样创建新数组。
定义min_run = 32
作为值有两个原因:
1. 使用插入排序对小数组进行排序非常快,并且min_run
利用此特性的价值很小。使用min_run
太大的值进行初始化将无法达到使用插入排序的目的,并使算法变慢。
2. 合并两个平衡列表比合并不成比例的列表要有效得多。min_run
在合并算法创建的所有不同运行时,选择一个为2的幂的值可确保更好的性能。
结合以上两个条件,可以提供几种min_run
选择。本教程中的实现min_run = 32
是其中一种可能性。
衡量Timsort的大O时间复杂性
平均而言,Timsort的复杂度为O(n log 2 n),就像合并排序和快速排序一样。对数部分来自执行每个线性合并操作的运行大小加倍。
但是,Timsort在已排序或接近排序的列表上表现特别出色,从而导致了O(n)的最佳情况。在这种情况下,Timsort明显胜过合并排序,并与快速排序的最佳情况相匹配。但是,对于Timsort来说,最糟糕的情况也是O(n log 2 n)。
对Timsort测量运行时间
调用时间运行测试函数:
执行时间:
0.51秒,Timsort比合并排序整整快0.1秒,即17%。它也比插入排序快11,000%!
现在,尝试使用这四种算法对已经排序的列表进行排序,然后看看会发生什么。
现在执行脚本,那么所有算法都将运行并输出相应的执行时间:
这次,Timsort的速度比合并排序快了37%,比快排快了5%,从而增强了利用已排序运行的能力。
请注意,Timsort如何从两种算法中受益,这两种算法单独使用时速度要慢得多。Timsort的神奇之处在于将这些算法结合起来并发挥其优势,以获得令人印象深刻的结果。
分析Timsort的优势和劣势
Timsort的主要缺点是它的复杂性。尽管实现了原始算法的非常简化的版本,但由于它同时依赖于insertion_sort()
和merge()
,因此仍需要更多代码。
Timsort的优点之一是其能够以O(n log 2 n)的方式执行预测,而与输入数组的结构无关。与快排相比,快排可以降级为O(n 2)。对于小数组,Timsort也非常快,因为该算法变成了单个插入排序。
对于现实世界中的使用(通常对已经具有某些预先存在的顺序的数组进行排序),Timsort是一个不错的选择。它的适应性使其成为排序任何长度的数组的绝佳选择。
结论
排序是任何Pythonista工具包中必不可少的工具。了解Python中不同的排序算法以及如何最大程度地发挥它们的潜力,你就可以实现更快,更高效的应用程序和程序!
文章开始前首先让我们来了解一下什么是Python。Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。重点是,Python 是一种对初学者非常友好的语言,从应用程序开发到文字处理、web甚至是游戏能提供支持,不少人也会将Python作为黑客攻击语言。
为何Python会被选用为黑客语言呢?支持功能强大的黑客攻击模块。如前所述,Python 的优点之一是拥有丰富多样的库。Python 提供多种库,用于支持黑客攻击,比如 pydbg、scapy、sqlmap、httplib 等。目前,这些库被广泛应用于各种黑客攻击。能够访问各种 API。Python 提供了 ctypes 库, 借 助 它, 黑 客 可 以 访 问 Windows、OS X、Linux、Solaris、FreeBSD、OpenBSD 等系统提供的 DLL 与共享库。大量黑客攻击工具提供 Python API。最具代表性的黑客攻击工具有 sqlmap、Nmap、Metasploit 等,它们都提供 Python 扩展接口。黑客使用 Python 可以将这些工具打造得更强大。易学易用。Python 语言易学易用,这对黑客攻击而言是个巨大的优势。一般来说,要成为一名黑客,必须掌握 3~4 种编程语言。Python 语言易学易用且拥有各种强大功能,这使它成为黑客攻击语言的不二之选。
Python 语言的优点目前,Python 在各领域都有着广泛的应用。由此可见,作为一种编程开发语言,Python 拥有众多优点,其语法简单易学且支持多种库,相同代码可以运行于多种平台。
■ 易学易用学习一种新编程语言时,往往会遇到各种各样的问题。为了解决这些问题,Python 语言做了大量努力。比如,Python 中不必声明变量类型,而在运行时动态确定。此外,也不需要用户对内存进行管理,这些工作由解释器自动执行。■ 功能强大Python 是开源语言,全世界开发人员一直在自发改进 Python,不断开发创建各种功能强大的库。其他语言中要使用数十行代码才能完成的功能,在 Python 中只需要使用简单的几行代码即可搞定。■ 扩展性良好Windows、UNIX、Mac、Android 操作系统都可以使用 Python,只需在目标操作系统中安装相应解释器即可。Python 内置多种编程接口,借助它可以在 Python 中使用其他语言开发的 API,对功能进行无限扩展。■ 开发速度快Python 语法简单,且拥有大量功能强大的库,与其他编程语言相比,使用Python 能够更快速地开发应用程序。在竞争激烈且对开发速度有严格要求的行业,使用 Python 进行开发是十分必要的。程序语言初期培训中,往往大量使用 Python 语言。因为 Python 语言易学,且拥有各种功能。网络上有大量关于学习 Python 的社区,从这些社区还能下载拥有丰富功能的各种模块。Python黑客攻击优点
从事黑客攻击需要具备三方面知识:第一是背景知识,需要理解语言结构、操作系统、网络、计算机体系结构等原理;第二必须能够熟练使用各种黑客攻击工具,寻找系统漏洞并实施攻击是一项重复性工作,灵活使用各种黑客攻击工具可以将这项工作自动化,并以人们易于理解的图形方式展现复杂的系统结构;第三必须掌握某种编程语言,无论黑客攻击工具多么强大,进行高难度黑客攻击时,必须亲自编写适合自己使用的工具,此时需要掌握编程语言。比如 Python语言,它具有如下优点:
支持功能强大的黑客攻击模块。如前所述,Python 的优点之一是拥有丰富多样的库。Python 提供多种库,用于支持黑客攻击,比如 pydbg、scapy、sqlmap、httplib 等。目前,这些库被广泛应用于各种黑客攻击。能够访问各种 API。Python 提供了 ctypes 库, 借 助 它, 黑 客 可 以 访 问 Windows、OS X、Linux、Solaris、FreeBSD、OpenBSD 等系统提供的 DLL 与共享库。大量黑客攻击工具提供 Python API。最具代表性的黑客攻击工具有 sqlmap、Nmap、Metasploit 等,它们都提供 Python 扩展接口。黑客使用 Python 可以将这些工具打造得更强大。易学易用。Python 语言易学易用,这对黑客攻击而言是个巨大的优势。一般来说,要成为一名黑客,必须掌握 3~4 种编程语言。其中最具代表性的是 C 语言与汇编语言,它们在分析系统与程序行为的过程中起着核心作用。此外,黑客还需要掌握另外一种编程语言,用于编写符合自身需要的黑客攻击工具。Python 语言易学易用且拥有各种强大功能,这使它成为黑客攻击语言的不二之选。作为黑客攻击语言,Python 拥有众多优点,初学者选择 Python 可以先人一步。Python 黑客攻击用途
Python 提供了丰富多样的模块,这些模块几乎可以直接用于所有黑客攻击领域。对于黑客攻击模块不提供的领域,可以借由 ctypes 调用操作系统提供的原生 API。简言之,使用 Python 几乎可以攻击所有领域,比如应用程序、Web、网络、系统等,下面分别介绍各领域 Python 黑客攻击技术。应用程序黑客攻击:可以向运行中的应用程序插入任意 DLL 或者源代码,拦截用户的键盘输入以盗取密码。此外,还可以将黑客攻击代码插入图片文件,在网络散布传播。Web 黑客攻击:可以创建网页爬虫,收集 Web 页面包含的链接,实现 SQL 注入,向处理用户输入的部分注入错误代码。使用 Python 可以实现简单的网络浏览器功能,通过操纵 HTTP 包,上传 Web shell 攻击所需文件。网络黑客攻击:可以实施网络踩点,搜索系统开放的端口,收集并分析网络上的数据包,进行网络嗅探。伪装服务器地址,实施 IP 欺骗攻击,非法盗取敏感信息。也可以大量发送数据包,实施拒绝服务式攻击,使服务器陷入瘫痪,无法正常对外提供服务。系统黑客攻击:黑客可以编写后门程序以控制用户 PC,开发用于搜索并修改 PC 注册表的功能。还可以利用应用程序的错误,通过缓冲区溢出或格式字符串实施攻击。那么,Python难学么?如果你是毫无基础,甚至英文和数学都不尽人意的人,那么自学这条路对你来说将会非常艰难。毕竟什么都不懂的情况下,往往会因为挫败感强而逐渐失去学习的兴趣。如果你了解一些基础知识,英文和逻辑也还可以的话,那么,不难!一点都不难。相比其他的编程语言,这简直就是“婴儿学步”的程度。只需要你做到以下几步。1、Python相关书籍若干本;2、了解Python基础数据类型;3、熟悉各种类型的操作方法;4、理解函数和类的概念。5、练习练习再练习,毕竟实践才能出成果嘛。学习Python需要多长时间?最快3-4个月,最慢一年,你就能流畅的使用这门编程语言去做你想做的项目。精通Python需要多长时间?任何知识都是基础入门比较快,达到精通的程序是需要时间的,这是一个逐渐激烈的过程。想要对一门语言得心应手,除了了解它之外,还需要通过大量的时间、大量的问题,来积累经验。不仅是看别人的源码,同时也将资金的源码分享出去。不断的动手去编写代码,不停的去实践,不停的去修改,不停的总结经验,最终才能熟能生巧,达到精通。如果有一天,当你遇到一个问题的时候,你能想出多种解决方法,并且迅速而准确的选出最有效率的那一个,就证明你已经对这门语言很精通了。说了这么多,Python怎么学呢?
如何一起学习,有没有免费资料?————————————————版权声明:本文为CSDN博主「AI女神」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/Python_GCS/article/details/82986042
以上是关于用Python手写五大经典排序算法,看完这篇终于懂了!的主要内容,如果未能解决你的问题,请参考以下文章