参考: 十大经典排序算法
概述
排序是按照某种顺序排列序列元素的一种算法。输出是输入序列的重新排序。
排序是计算机科学中的重要算法,排序有时可以显著降低问题的复杂度,可以使用排序作为减少查找复杂度的一种技术。
排序的分类
基于参数分类
- 比较次数
- 交换次数
- 内存使用
- 递归
- 稳定性
- 适应性
其他分类
- 内部排序 排序时仅使用主存储器的排序算法称为内部排序(internal sort)
- 外部排序 排序时需要使用磁盘等外部存储器的排序算法属于外部排序(external sort)
总结几个经典的排序算法
复杂度比较
| 方法 | 平均时间 | 最坏时间 | 最好时间 | 空间 | 稳定性 |
|---|---|---|---|---|---|
| 插入 | 稳定 | ||||
| 希尔 | 不稳定 | ||||
| 选择 | 不稳定 | ||||
| 堆 | 不稳定 | ||||
| 冒泡 | 稳定 | ||||
| 快速 | 不稳定 | ||||
| 归并 | 稳定 | ||||
| 计数 | 稳定 | ||||
| 桶 | 稳定 | ||||
| 基数 | 稳定 |
排序算法均以升序排列为例
插入排序(Selection Sort)
计算过程
- 将第一待排序序列第一个元素看做一个有序序列,将第二个元素到最后元素作为未排序序列
- 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置(如果与有序队列的某个元素相等,则放到此元素后面)
代码
def insertion_sort(arr):
"""
插入排序
:param arr 待排序的数组
"""
for i in range(1, len(arr)):
preIndex = i - 1
current = arr[i]
while preIndex >= 0 and arr[preIndex] > current:
arr[preIndex + 1] = arr[preIndex]
preIndex -= 1
arr[preIndex + 1] = current
return arr
选择排序(Selection Sort)
计算过程
- 在序列中找到最小的元素,交换到序列的起始位置
- 将需要比较起始位置后移
- 重复1~2步,知道比较完
代码
def selection_sort(arr):
"""
选择排序
:param arr 待排序的数组
"""
for i in range(len(arr) - 1):
minIndex = i
for j in range(i + 1, len(arr))
# 交换顺序
if arr[j] < arr[minIndex]:
minIndex = j
if i != minIndex:
# 交换
arr[i], arr[minIndex] = arr[minIndex], arr[i]
return arr
冒泡排序(Bubble Sort)
计算过程
- 比较相邻的元素,如果第一个比第二个大,就交换顺序
- 对每一个相邻的元素重复上一步,这样执行完一遍,最后一个元素为最大
- 递减每次需要比较的元素
- 重复1~3步,直到递减为0
复杂度分析
| 最快情况 | 最慢情况 |
|---|---|
| 数据为正序 | 数据为反序 |
代码
def bubble_sort(arr):
"""
冒泡排序
:param arr 待排序的数组
"""
# 用i来递减每次需要比价的元素
for i in range(1, len(arr)):
for j in range(0, len(arr) - i)
# 交换顺序
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[i]
return arr
希尔排序(Shell Sort)
希尔排序 希尔排序是基于插入排序改进的:
- 插入排序比较低效,因为每次插入排序只能移动一位
基本思想:先将整个待排序的记录序列分割为若干子序列,分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。
计算过程
- 选择一个增量序列,其中
- 按增量序列个数k,对序列进行k趟排序
- 每趟排序,根据对应的增量,将待排序分割成若干长度为m的子序列,分别对各子表进行直接插入排序。仅增量因子为1时,整个序列作为一个表来处理,表长度即为整个序列的长度。
希尔排序的思想是使数组中任意间隔为h的元素都是有序的。这样的数组被称为h有序数组。换句话说,一个h有序的数组就是h个互相独立的有序数组编织在一起组成的一个数组。
代码
两种实现
def shell_sort(arr):
"""
希尔排序
:param arr 待排序的数组
"""
n = len(arr)
# 初始步长
gap = n / 2
while gap > 0:
for i in range(gap, n):
# 每个步长进行插入排序
temp = arr[i]
j = i
# 插入排序
while j >= 0 and arr[j - gap] > temp:
arr[j] = arr[j - gap]
j -= gap
arr[j] = temp
gap = gap / 2
return arr
快速排序(Quick Sort)
快速排序是对冒泡排序的一种改进,本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。
计算过程
- 从数列中挑出一个元素,称为基准(pivot)
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序
代码
精简版本(占用多余空间)
def quick_sort(arr):
"""
快速排序
:param arr 待排序的数组
"""
if len(arr) <= 1:
return arr
left, right, mid = [], [], []
pivot = random.choice(arr)
for item in arr:
if item == pivot:
mid.append(item)
elif item < pivot:
left.append(item)
else:
right.append(item)
# 递归
return quick_sort(left) + mid + quick_sort(right)
原地排序版本
def quick_sort(arr, start, end):
if start >= end:
return arr
mid_value = arr[first]
low = start
high = end
while low < high:
while low < high and arr[high] >= mid_value:
high -= 1
arr[low] = arr[high]
while low < high and arr[low] < mid_value:
low += 1
arr[high] = arr[low]
arr[low] = mid_value
quick_sort(arr, start, low - 1)
quick_sort(arr, low + 1, high)