一.排序算法介绍
1.什么是排序
排序就是对一序列对象根据某个关键字进行排序。假设含有n个记录的序列为{r1,r2,……,rn},其相应的关键字分别为{k1,k2,……,kn},需确定 1,2,……,n的一种排列p1,p2,……,pn,使其相应的关键字满足kp1≤kp2≤……≤kpn(非递减或非递增)关系,即使得序列成为一个按关键字有序的序列{rp1,rp2,……,rpn},这样的操作就称为排序。
2.排序算法常用术语
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
内排序:所有排序操作都在内存中完成;
外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
时间复杂度: 一个算法执行所耗费的时间。
空间复杂度:运行完一个程序所需内存的大小
3.排序算法效率比较
4.排序算法分类
二.部分排序算法代码实现
1.冒泡排序
算法思想:冒泡排序(Bubble Sort)一种交换排序,它的基本思想是:两两比较相邻记录的
关键字,如果反序则交换,直到没有反序的记录为止。这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端。
算法描述:
1.比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。遍历结束后,最后的元素会是最大的数。
3. 此时最后一个元素是所有元素中最大的元素,其他元素依旧是无序的,针对其余无序元素重复步骤1、2,直到剩最后一个元素不需要比较。
python代码实现
def bubble_sort(arr):
n = len(arr)
for i in range(1,n):
for j in range(0,n-i):
if arr[j]>arr[j+1]:
arr[j],arr[j+1] = arr[j+1],arr[j]
注意:在冒泡排序中,如果有n个数要排序,那么一共需要比较n-1趟,每一趟比较的次数为n-i,i为第几趟。举例说明:有5个数(n=5)要排序,则第一躺(i=1),5个数两两相比,比较四次(n-i=4),第一趟结束后最大的数到最后;第二趟(i=2),剩余4个数两两相比,比较三次(n-i=5-2=3);重复比较的过程,直到第四趟剩余最后两个数,比较之后得到排序结果。
算法复杂度分析
最好的情况,也就是要排序的表本身就是有序的,那么我们比较次数,可以推断出就是
n‐1 次的比较,没有数据交换,时间复杂度为O(n)。
最坏的情况,即待排序表是逆序的情况,此时需要比较1+2+3+4+…+(n-1)=n(n-1)/2次,
即时间复杂度为O(n**2)。
稳定性: 稳定
2.快速排序
算法思想:快速排序(Quick Sort)的基本思想是:通过一趟排序将待排记录分割成独立的
两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部
分记录继续进行排序,以达到整个序列有序的目的。
算法描述:
1.从数列中挑出一个元素,称为 “基准”(pivot)
2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。
3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
代码实现
def quickSort(arr):
if len(arr)<2:
return arr
else:
pivot=arr[0]
less = [i for i in arr[1:] if i<=pivot]
greater = [i for i in arr[1:] if i>=pivot]
return quickSort(less) + [pivot] +quickSort(greater)
算法复杂度分析
层数为O(logn)(即调用栈的高度为O(logn)),而每层需要的时间为O(n)。因此整个算法需要的时间为O(n) * O(logn) = O(nlogn)
3.直接插入排序
算法思想:
插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
算法描述:
1.a[0]为有序区,待排序区为a[1…n-1]。令i=1。
2.将a[1]与a[0]中元素比较,将小的元素放在第一个位置。
3.以此类推,直到待排序中全部元素插入完成为止。
代码实现:
def insert_sort(nums):
count = len(nums)
for i in range(1, count):
key = nums[i]
j = i - 1
while j >= 0:
# 比较大小
if nums[j] > key:
nums[j + 1] = nums[j]
nums[j] = key
j -= 1
return nums
算法复杂度分析:
最好的情况,也就是要排序的表本身就是有序的, 因此没有移动的记录,时间复杂
度为 O(n)。
最坏的情况,即待排序表是逆序的情况,时间复杂度为 O(n**2)。
4.选择排序
算法思想:
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
算法描述:
1.初始状态:无序区为R[1…n],有序区为空;
2.第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1…i-1]和R(i…n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1…i]和R[i+1…n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
3.n-1趟结束,数组有序化了。
代码实现:
#定义函数,找出无序数组中的最大(小)值
def findSmallest(arr):
smallest = arr[0]
smallest_index = 0
for i in range(1, len(arr)):
if arr[i] < smallest:
smallest = arr[i]
smallest_index = i
return smallest_index
def selectSort(arr):
newArr = []
for i in range(len(arr)):
smallest = findSmallest(arr)
newArr.append(arr.pop(smallest))
return newArr
算法复杂度分析:
最优时间复杂度:O(n2 )
最坏时间复杂度:O(n2 )
稳定性:不稳定(考虑升序每次选择最大的情况)
5.希尔排序
算法思想:
算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的然后
再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1时,进行直接插入排序后,排序完成。
希尔排序是D.L.Shell于 1959 年提出来的一种排序算法,在这之前排序算法的时间复杂度基本
都是O(n2)的,希尔排序算法是突破这个时间复杂度的第一批算法之一。
算法描述:
如图,第一趟排序取增量5(10个数排序,n/2),则第一个数49与13比较,49大于13,49与13交换;第二个数38与27比较,不交换;循环执行,直到比较到最后一个数04
第二趟比较取增量3,13与55比较,27与65比较,循环向后执行
第三趟比较取增量1,相邻两个数进行比较
算法复杂度分析:
最佳情况:T(n) = O(nlogn) 最坏情况:T(n) = O(nlog n) 平均情况:T(n) =O(nlog n)
6.基数排序
算法思想:
基数排序(radix sort)它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog®m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。
算法描述:
1.分配,先从个位开始,根据位值(0-9)分别放到0~9号桶中(比如53,个位为3,则放入3号桶中)
2.收集,再将放置在0~9号桶中的数据按顺序放到数组中