排序算法python实现

Python018

排序算法python实现,第1张

排序算法是《数据结构与算法》中最基本的算法之一。

排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。用一张图概括:

点击以下图片查看大图:

关于时间复杂度

平方阶 (O(n2)) 排序 各类简单排序:直接插入、直接选择和冒泡排序。

线性对数阶 (O(nlog2n)) 排序 快速排序、堆排序和归并排序;

O(n1+§)) 排序,§ 是介于 0 和 1 之间的常数。 希尔排序

线性阶 (O(n)) 排序 基数排序,此外还有桶、箱排序。

关于稳定性

稳定的排序算法:冒泡排序、插入排序、归并排序和基数排序。

不是稳定的排序算法:选择排序、快速排序、希尔排序、堆排序。

名词解释:

n:数据规模 k:"桶"的个数 In-place:占用常数内存,不占用额外内存 Out-place:占用额外内存 稳定性:排序后 2 个相等键值的顺序和排序之前它们的顺序相同

包含以下内容:

1、冒泡排序 2、选择排序 3、插入排序 4、希尔排序 5、归并排序 6、快速排序 7、堆排序8、计数排序 9、桶排序 10、基数排序

排序算法包含的相关内容具体如下:

冒泡排序算法

冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。

选择排序算法

选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n?) 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间。

插入排序算法

插入排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了,因为只要打过扑克牌的人都应该能够秒懂。插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

希尔排序算法

希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。

归并排序算法

归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

快速排序算法

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。

堆排序算法

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。

计数排序算法

计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。

桶排序算法

桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。

基数排序算法

基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。

主要原因,内置函数用C写的。在Python语言内无论如何造不出内置函数的轮子。这也是通常C跟C++语言用户更喜欢造基础算法的轮了的原因。因为C/C++用户真有条件写出匹敌标准库的算法,但很多高级语言不行,不是程序员技术差,是客观条件就根本做不到。

你比如说Java语言没人造字符串的轮子,C++光一个字符串类就有无数多的实现。是因为C+用户更喜欢写字符串类吗?显然不是,一方面是因为Java语言内没法造出匹敌Java内置标准库算法的轮子,而C++真的可以,另外一个比较惨的原因是C++标准库的字符串功能太弱了,大多数高级语言的字符串类功能都比C+标准库字符串类功能更强。

写C++的时候一大错觉就是我觉着我能比标准库还快,同样的道理放在Python里面也同样适用,不管是Python各种常用package或内建函数,基本上都针对实用场景作了优化,自己手写的算法一般是比不上内建算法效率的,这也是为什么用Python时不鼓励自己造轮子的原因。

回到这个问题,Python内建的sort本质上为C实现的函数,本身执行效率就会比Python快很多,并且会根据不同的数据规模采用不同的排序算法,故效率一般都会优于自己在Python里面手写的排序更何况题主写的是基于递归的quicksort9,额外时间开销大。

因为python内置的sort是用c语言写的,如果你用c语言或者c++写的话肯定是可以做到一样快的至于为什么python计算效率比c语言能慢100倍这个具体的原理我不清楚,不过鉴于知乎上已经有很多大佬解释过这个问题,我就不在这里班门弄斧了

还有底下扯timsort的,快排序是所有比较排序算法里平均性能最优的一族算法,像C++和rust里的unstable_sort都是用的快排序。可能在一些情况下,比如数组几乎有序时,timsort会比快排序快。但是你随便给一个数组,比如像题主那样随机一个一百万大小的数然后排序,timsort是绝对不可能比快排序快的。绝对不可能。快的这100倍和timsort屁关系都没有。

我是C/C++程序员,我可以很负责的告诉你,在用天下现有所有高级语言进行排序的问题上,C要是认了第二,则没人敢认第一。所以,我猜,Python以及好多其他高级语言,都会时不时直接上C语言写的静态库和动态库。我自己也造了不少轮子,有部分是因为刚刚起步,对系统API和函数库不熟悉,找不到适合的,所以自己造轮子,后来发现了有更好的,我把我写的抛弃了。但这里也不排除有一部分是因为我个人觉得还有优化的空间,所以自己用C语言重新造了一个轮子,这样效率比现成的更优。

所以说,要论高级语言的鼻祖,还真非C莫属,从执行效率上讲,别说python,JAVA,C#,VB,甚至C的亲儿子C++,在同一个程序员手中,都没法与C抗衡,所以说,这些语言都是排着队等着被C吊打的,也正因为如此,所以,像python这类高级语言,有自带函数可用的,最好别想着自己重新造轮子,因为你不可能造出比自带函数更快的轮子。

内置库函数都是用C实现的,肯定要比手写的Python程序执行效率更高,此外内置排序Timsort相比本科课程上学的时间复杂度为Onlogn的排序算法做了很多常数优化,所以对于普通人而言,不要希望纯手写出来的东西效率能和标准库相当了。另外,题主写的排序是过不了LeetCode上的裸排序题目的,随机选取pivot对于快速排序是最基本的优化虽然题主排的是随机数,现在这么选肯定不是效率低的主要原因。

所以说了,py几乎得把自己的循环体拆了,这就是py和c/c++的性能差距,必须尽量用内置函数和numpy来处理数据,一旦手写循环体。,那你就得知道这可能得慢百倍,像用opency的py版时你不小心写个双循环来处理数据,那酸爽,而cppc#搞opencv就能随意用指针来写循环,这也是为啥他们其实不需要numpy这种组件,自身就有足够的性能和灵活度来处理这个。

Cpp内置的排序是快排和堆排的结合,最坏时间复杂度为nlogn,而快排最坏是n2。至于python内部的排序,我认为是一个道理,不会简简单单是一个快排,举个简单例子,当你数据已经是有序的时候,再传入快排肯定就不合适。那你设置排序函数的时候,是不是预先将他打乱,再进行快排会更好呢。当然具体不会这么简单,只是我认为官方给的接口都是很精妙的,很值得学习。

一方面Python中sort函数是用C语言写的,C++内部的sort是由快排,直接插入和堆排序混合的,当数据量比较大的时候先用的快排,当数据量小的时候用直接插入,因为当数据量变小时,快排中的每个部分基本有序,接近直接插入的最好情况的时间复杂度O(n),就比快排要好一点了。

另外一方面这个的底层实现就是归并排序。,只是使用了Python无法编写的底层实现,从而避免了Python本身附加的大量开销,速度比我们自己写的归并排序要快很多,所以说我们一般排序都尽量使用sorted和sort。