十大经典排序算法解析(Python实现)
十大经典排序算法解析
序算法是《数据结构与算法》中最基本的算法之一。
排序算法可以分为内部排序和外部排序。
内部排序是数据记录在内存中进行排序。
而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。
常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。
用一张图概括:
时间复杂度与空间复杂度
关于时间复杂度:
平方阶 (O(n2)) 排序 各类简单排序:直接插入、直接选择和冒泡排序。
线性对数阶 (O(nlog2n)) 排序 快速排序、堆排序和归并排序;
O(n1+§)) 排序,§ 是介于 0 和 1 之间的常数。 希尔排序
线性阶 (O(n)) 排序 基数排序,此外还有桶、箱排序。
关于稳定性:
稳定的排序算法:冒泡排序、插入排序、归并排序和基数排序。
不是稳定的排序算法:选择排序、快速排序、希尔排序、堆排序。
1. 冒泡排序
1.1 算法步骤
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
1.2参考代码
def bubble_sort3(arr):
for j in range(len(arr)-1, 0, -1):
count = 0
for i in range(0, j):
if arr[i] > arr[i + 1]:
arr[i], arr[i + 1] = arr[i + 1], arr[i]
count += 1
if count == 0:
return
bubble_sort3(arr)
print(arr) # [1, 3, 4, 7, 8, 34, 67]
2. 选择排序
2.1 算法步骤
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
重复第二步,直到所有元素均排序完毕。
2.2参考代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author : xiaoke
def select_sort(alist):
"""选择排序"""
n = len(alist)
# 外层循环控制循环次数
for j in range(0, n - 1):
# 假设找到的最小元素下标为j
min_index = j
# 寻找最小元素的过程
for i in range(j + 1, n):
# 假设最小下标的值,大于循环中一个元素,那么就改变最小值的下标
if alist[min_index] > alist[i]:
min_index = i
# 为了容错处理,因为循环一开始就假设把最小值的下标j赋值给变量min_index
if j != min_index:
# 在不停的循环中,不停的交换两个不一样大小的值
alist[min_index], alist[j] = alist[j], alist[min_index]
if __name__ == '__main__':
alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
print("原列表为:%s" % alist)
select_sort(alist)
print("新列表为:%s" % alist)
# 结果如下:
# 原列表为:[54, 26, 93, 17, 77, 31, 44, 55, 20]
# 新列表为:[17, 20, 26, 31, 44, 54, 55, 77, 93]
3. 插入排序
3.1 算法步骤
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
3.2代码参考
lst = [4,5,6,3,2,1]
def insertSort(arr):
for i in range(1,len(arr)):
j = i-1
key = arr[i]
while j >= 0:
if arr[j] > key:
arr[j+1] = arr[j]
arr[j] = key
j -= 1
return arr
print(insertSort(lst))
4. 希尔排序
4.1 算法步骤
选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
按增量序列个数 k,对序列进行 k 趟排序;
每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
4.2代码参考
def shell_sort(alist):
"""希尔排序"""
n = len(alist)
gap = n // 2
while gap >= 1:
for j in range(gap, n):
i = j
while (i - gap) >= 0:
if alist[i] < alist[i - gap]:
alist[i], alist[i - gap] = alist[i - gap], alist[i]
i -= gap
else:
break
gap //= 2
if __name__ == '__main__':
alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
print("原列表为:%s" % alist)
shell_sort(alist)
print("新列表为:%s" % alist)
# 结果如下:
# 原列表为:[54, 26, 93, 17, 77, 31, 44, 55, 20]
# 新列表为:[17, 20, 26, 31, 44, 54, 55, 77, 93]
5. 归并排序
5.1 算法步骤
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
设定两个指针,最初位置分别为两个已经排序序列的起始位置;
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
重复步骤 3 直到某一指针达到序列尾;
将另一序列剩下的所有元素直接复制到合并序列尾。
5.2代码参考
import random
def ConfiationAlgorithm(str):
if len(str) <= 1: #子序列
return str
mid = (len(str) / 2)
left = ConfiationAlgorithm(str[:mid])#递归的切片操作
right = ConfiationAlgorithm(str[mid:len(str)])
result = []
#i,j = 0,0
while len(left) > 0 and len(right) > 0:
if (left[0] <= right[0]):
#result.append(left[0])
result.append(left.pop(0))
#i+= 1
else:
#result.append(right[0])
result.append(right.pop(0))
#j+= 1
if (len(left) > 0):
result.extend(ConfiationAlgorithm(left))
else:
result.extend(ConfiationAlgorithm(right))
return result
if __name__ == '__main__':
a = [20,30,64,16,8,0,99,24,75,100,69]
print ConfiationAlgorithm(a)
b = [random.randint(1,1000) for i in range(10)]
print ConfiationAlgorithm(b)
6. 快速排序
6.1 算法步骤
从数列中挑出一个元素,称为 “基准”(pivot);
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
6.2代码参考
def quick_sort(array, left, right):
if left >= right:
return
low = left
high = right
key = array[low]
while left < right:
while left < right and array[right] > key:
right -= 1
array[left] = array[right]
while left < right and array[left] <= key:
left += 1
array[right] = array[left]
array[right] = key
quick_sort(array, low, left - 1)
quick_sort(array, left + 1, high)
7. 堆排序
7.1 算法步骤
创建一个堆 H[0……n-1];
把堆首(最大值)和堆尾互换;
把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
重复步骤 2,直到堆的尺寸为 1。
7.2代码参考
def HeapSort(input_list):
#调整parent结点为大根堆
def HeapAdjust(input_list,parent,length):
temp = input_list[parent]
child = 2*parent+1
while child < length:
if child+1 <length and input_list[child] < input_list[child+1]:
child +=1
if temp > input_list[child]:
break
input_list[parent] = input_list[child]
parent = child
child = 2*child+1
input_list[parent] = temp
if input_list == []:
return []
sorted_list = input_list
length = len(sorted_list)
#最后一个结点的下标为length//2-1
#建立初始大根堆
for i in range(0,length // 2 )[::-1]:
HeapAdjust(sorted_list,i,length)
for j in range(1,length)[::-1]:
#把堆顶元素即第一大的元素与最后一个元素互换位置
temp = sorted_list[j]
sorted_list[j] = sorted_list[0]
sorted_list[0] = temp
#换完位置之后将剩余的元素重新调整成大根堆
HeapAdjust(sorted_list,0,j)
print('%dth' % (length - j))
print(sorted_list)
return sorted_list
if __name__ == '__main__':
input_list = [50,123,543,187,49,30,0,2,11,100]
print("input_list:")
print(input_list)
sorted_list = HeapSort(input_list)
print("sorted_list:")
print(input_list)
8. 计数排序
8.1 算法步骤
花O(n)的时间扫描一下整个序列 A,获取最小值 min 和最大值 max
开辟一块新的空间创建新的数组 B,长度为 ( max - min + 1)
数组 B 中 index 的元素记录的值是 A 中某元素出现的次数
最后输出目标整数序列,具体的逻辑是遍历数组 B,输出相应元素以及对应的个数
8.2代码参考
def countsort(lista):
leng=len(lista);
c=[];
res=[];
for i in range(0,100):
c.append(0);
for i in range(0,leng):
c[lista[i]]=c[lista[i]]+1;
res.append(0);
for i in range(0,100):
c[i]=c[i-1]+c[i]; #c中此时存放的是小于或者等于i的数字的个数
for i in range(leng-1,-1,-1):
res[c[lista[i]]-1]=lista[i];
c[lista[i]]=c[lista[i]]-1;
return res;
lista=[5,4,2,5,1,7]; #计数排序测试代码
lista=countsort(lista);
print lista;
9. 桶排序
9.1 算法步骤
设置固定数量的空桶。
把数据放到对应的桶中。
对每个不为空的桶中数据进行排序。
拼接不为空的桶中数据,得到结果
9.2代码参考
class node:
def __init__(self,k):
self.key=k;
self.next=None;
def bucketsort(lista):
h=[];
for i in range(0,10):
h.append(node(0));
for i in range(0,len(lista)):
tmp=node(lista[i]);
map=lista[i]/10;
p=h[map];
if p.key is 0:
h[map].next=tmp;
h[map].key=h[map].key+1;
else:
while(p.next !=None and p.next.key<=tmp.key):
p=p.next;
tmp.next=p.next;
p.next=tmp;
h[map].key=h[map].key+1;
k=0;
for i in range(0,10):
q=h[i].next;
while(q != None ):
lista[k]=q.key;
k=k+1;
q=q.next;
return lista;
lista=[1,4,23,45,97,22,10,4]; #桶排序测试代码
bucketsort(lista);
print lista;
10. 基数排序
10.1 算法步骤
将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零
从最低位开始,依次进行一次排序
从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列
10.2参考代码
def radix_sort(list, d=3): # 默认三位数,如果是四位数,则d=4,以此类推
for i in range(d): # d轮排序
s = [[] for k in range(10)] # 因每一位数字都是0~9,建10个桶
for j in list:
s[int(j / (10 ** i)) % 10].append(j)
re = [a for b in s for a in b]
return re
上一篇: 机器学习之聚类算法:密度聚类BDscan,整理ing
下一篇: 机器学习算法总结