快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。
快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高!它是处理大数据最快的排序算法之一了。
虽然 Worst Case 的时间复杂度达到了 O(n²),但是人家就是优秀,在大多数情况下都比平均时间复杂度为 O(n logn) 的排序算法表现要更好,可是这是为什么呢,我也不知道。好在我的强迫症又犯了,查了 N 多资料终于在《算法艺术与信息学竞赛》上找到了满意的答案:
快速排序的最坏运行情况是 O(n²),比如说顺序数列的快排。但它的平摊期望时间是 O(nlogn),且 O(nlogn) 记号中隐含的常数因子很小,比复杂度稳定等于 O(nlogn) 的归并排序要小很多。所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
def quick_sort(list1, start, end):
if start < end:
i = ( start-1 )
lei = list1[end]
for j in range(start , end):
if list1[j] <=lei:
i = i+1
list1[i],list1[j] = list1[j],list1[i]
list1[i+1],list1[end] = list1[end],list1[i+1]
pi=i+1
quick_sort(list1, start, pi-1)
quick_sort(list1, pi+1, end)
li = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48]
quick_sort(li, 0, len(li)-1)
print(li)
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法:
1.大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
2.小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列。
堆排序的平均时间复杂度为 Ο(nlogn)。
1. 算法步骤: i.创建一个堆 H[0……n-1]; ii.把堆首(最大值)和堆尾互换; iii.把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置; iv.重复步骤 2,直到堆的尺寸为 1。def buildMaxHeap(arr):
import math
for i in range(math.floor(len(arr)/2),-1,-1):
heapify(arr,i)
def heapify(arr, i):
min1 = 2*i+1
max1 = 2*i+2
now = i
if min1 < leng and arr[min1] > arr[now]:
now = min1
if max1 < leng and arr[max1] > arr[now]:
now = max1
if now != i:
swap(arr, i, now)
heapify(arr, now)
def swap(arr, i, j):
arr[i], arr[j] = arr[j], arr[i]
def heapSort(arr):
global leng
leng = len(arr)
buildMaxHeap(arr)
for i in range(len(arr)-1,0,-1):
swap(arr,0,i)
leng -=1
heapify(arr, 0)
return arr
arr = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48]
heapSort(arr)
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
def countingSort(arr, maxValue):
list1 = []
a = [0 for _ in range(0, maxValue + 1)]
for x in arr:
a[x] += 1
for f in range(0, len(a)):
while a[f] > 0:
list1.append(f)
a[f] -= 1
arr=list1
return arr
arr = [2,3,8,7,1,2,2,2,7,3,9,8,2,1,4,2,4,6,9,2]
countingSort(arr, len(arr))
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点:
i.在额外空间充足的情况下,尽量增大桶的数量。
ii.使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中。
同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。
什么时候最快
当输入的数据可以均匀的分配到每一个桶中。
什么时候最慢
当输入的数据被分配到了同一个桶中。
def bucket_sort(s):
min1 = min(s)
max1 = max(s)
r = (max1-min1) / len(s)
list1 = [ [] for x in range(len(s) + 1)]
for x in s:
list1[int((x-min1)//r)].append(x)
s.clear()
for x in list1:
for d in sorted(x):
s.append(d)
a = [3.2,6,8,4,2,6,7,3]
bucket_sort(a)
print(a) # [2, 3, 3.2, 4, 6, 6, 7, 8]
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
1.基数排序 vs 计数排序 vs 桶排序 基数排序有两种方法: 这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异:基数排序:根据键值的每位数字来分配桶;
计数排序:每个桶只存储单一键值;
桶排序:每个桶存储一定范围的数值。
def RadixSort(list1):
i,n,max1 = 0,1,max(list1)
while max1 > 10**n:
n += 1
while i < n:
b = {}
for x in range(10):
b.setdefault(x, [])
for x in list1:
r =int((x / (10**i)) % 10)
b[r].append(x)
j = 0
for c in range(10):
if len(b[c]) != 0:
for x in b[c]:
list1[j] = x
j += 1
i += 1
return list1
list1 = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48]
RadixSort(list1)
本文仅供参考,如有错误,敬请指正。