常用排序算法比较
Posted 夏小悠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了常用排序算法比较相关的知识,希望对你有一定的参考价值。
文章目录
前言
本篇博客记录一下常见的排序算法及其实现代码。
1. 插入排序
1.1 直接插入排序
算法思路:先将序列的第一个记录看成一个有序
的子序列,然后从第二个记录开始逐个进行插入,直至整个序列有序为止。相当于就是将整个序列划分为有序部分和无序部分,每次从序列的无序部分取出第一个记录,把它插入序列的有序部分的合适位置,是序列的有序部分仍然保持有序。
1.2 折半插入排序
算法思路:每次从序列的无序部分取出第一个记录,将其与序列的有序部分中间位置的记录进行比较,如果比中间位置上记录的关键字大,则在该关键字的右边按上述方式继续进行比较;否则在该关键字的左边按上述方式继续进行比较。直到找到合适的插入位置,并将序列的有序部分的该位置之后的记录向后移一位,最终将该记录插入上述位置,是该序列的有序部分仍然保持有序。
1.3 希尔排序
算法思路:先将待排序记录划分为若干个子序列,并对这些子序列进行直接插入排序,待整个序列基本有序时,再对其进行直接插入排序。
2. 交换排序
2.1 冒泡排序
算法思路:在每一趟排序时,把序列的无序部分中的第一个记录的关键字与第二个记录的关键字进行比较,若为逆序,则交换这两个记录的位置,然后对序列的无序部分中的第二个纪录与第三个记录重复上述步骤,以此类推,当对序列无序部分中的倒数第二个纪录与最后一个记录执行完上述步骤之后,则完成一趟冒泡排序,此时无序部分中的最后一个记录成为有序部分的第一个记录。当该序列的无序部分仅有一个纪录时,算法结束。
2.2 快速排序
算法思路:将序列中的某一个记录设定为枢轴(通常将第一个记录设为枢轴),通过一趟排序后,把序列分为左子序列和右子序列两个子序列;将不小于枢轴的记录移动到右子序列中,并将小于枢轴的记录移动到左子序列中;再对左子序列和右子序列分别重复执行上述步骤,直到快速排序结束。
3. 选择排序
3.1 简单选择排序
算法思路:将含有 n n n个记录的待排序序列分为有序和无序两个部分,初始时有序部分中的记录个数为0,而无序部分则包含序列中的 n n n个记录;在每一趟排序中,依次在无序部分中找出最小的记录,将其作为有序部分的第 i i i个记录,直到无序部分记录的总个数为1,此时直接将该记录作为有序部分的最后一个记录。
3.2 堆排序
小根堆
:堆顶记录为序列中关键字最小的记录;大根堆
:堆顶记录为序列中关键字最大的记录。
算法思路(以大根堆为例):首先将含有 n n n个记录的序列分为有序部分和无序序列;初始时,有序部分含有0个记录,而无序部分则包含 n n n个记录;在第一趟堆排序前,将含有 n n n个记录的无序部分调整为大根堆,然后将堆中的第1个记录与第 n n n个记录交换位置,交换后堆中的第 n n n个记录即为序列中关键字最大的记录,它被作为有序部分中的第1个记录,此时无序部分总记录数减1;在第二趟堆排序开始前,须将无序部分的 n − 1 n-1 n−1个记录(即从第1个记录至第 n − 1 n-1 n−1个记录)调整为大根堆,重复执行上述步骤,直到堆中只剩下一个记录,排序结束。
4. 归并排序
归并排序是指将两个或两个以上的有序序列
合并为一个新的有序序列。
算法思路:对于一个含有
n
n
n个记录的序列
L
L
L,首先将该序列看做
n
n
n个有序的子序列,每个子序列中只包含1个记录,然后对其进行两两归并,得到
⌈
n
2
⌉
\\lceil \\frac {n} {2}\\rceil
⌈2n⌉个有序的子序列,其中每个子序列可能含有2个或1个(当
n
n
n为奇数时),重复上述操作,直到序列
L
L
L有序。
5. 代码测试
class ListItem(object):
def __init__(self, key, info):
self.key = key
self.info = info
class MySort(object):
def __init__(self, data_list):
self.length = len(data_list)
self.SeqList = [ListItem(0, 0)]
for index in range(self.length):
self.SeqList.append(ListItem(data_list[index], index + 1))
def InsertSort(self):
"""
插入排序之直接插入排序
:return:
"""
for i in range(2, self.length):
# 0哨兵 暂存待插入的数据
self.SeqList[0].key = self.SeqList[i].key
j = i
while self.SeqList[0].key < self.SeqList[j-1].key:
# 哨兵的值小, 即后者比前者小
# 后移
self.SeqList[j].key = self.SeqList[j-1].key
j -= 1
# 将哨兵的值放在合适的位置
self.SeqList[j].key = self.SeqList[0].key
def BinInsertSort(self):
"""
插入排序之折半插入排序
:return:
"""
for i in range(2, self.length):
# 0哨兵 暂存待插入的数据
self.SeqList[0].key = self.SeqList[i].key
low = 1
high = i - 1
while low <= high:
# 折半 取整
mid = (low + high) // 2
if self.SeqList[0].key < self.SeqList[mid].key:
# 左区间
high = mid - 1
else:
# 右区间
low = mid + 1
j = i - 1
while j >= low:
# 后移
self.SeqList[j+1].key = self.SeqList[j].key
j -= 1
self.SeqList[low].key = self.SeqList[0].key
def BubbleSort(self):
"""
交换排序之冒泡排序
:return:
"""
for i in range(1, self.length - 1):
for j in range(1, self.length - 1):
if self.SeqList[j+1].key < self.SeqList[j].key:
# 后者小, 交换
temp = self.SeqList[j].key
self.SeqList[j].key = self.SeqList[j+1].key
self.SeqList[j+1].key = temp
def BubbleSortUpdate(self):
"""
交换排序之冒泡排序 改进版
:return:
"""
for i in range(1, self.length - 1):
flag = 0
for j in range(1, self.length - 1):
if self.SeqList[j+1].key < self.SeqList[j].key:
# 后者小, 本趟需要交换
flag = 1
temp = self.SeqList[j].key
self.SeqList[j].key = self.SeqList[j+1].key
self.SeqList[j+1].key = temp
if flag == 0:
break
def SimpleSelectSort(self):
"""
选择排序值简单选择排序
:return:
"""
for i in range(1, self.length):
minpos = i
for j in range(i+1, self.length):
if self.SeqList[j].key < self.SeqList[minpos].key:
# 从待排序中选择最小的
minpos = j
if minpos != i:
temp = self.SeqList[minpos].key
self.SeqList[minpos].key = self.SeqList[i].key
self.SeqList[i].key = temp
def myprint(self):
for i in range(1, self.length + 1):
print(self.SeqList[i].key, end=' ')
print('')
if __name__ == '__main__':
datas = [101, 483, 206, 156, 423, 366, 624]
mysort = MySort(data_list=datas)
# mysort.InsertSort()
# mysort.BinInsertSort()
# mysort.BubbleSort()
# mysort.BubbleSortUpdate()
mysort.SimpleSelectSort()
mysort.myprint()
算法 | 时间复杂度(最好、最坏、平均) | 空间复杂度 | 是否稳定 | 特点 |
---|---|---|---|---|
直接插入 | O ( n ) 、 O ( n 2 ) 、 O ( n 2 ) O(n)、O(n^2)、O(n^2) O(n)、O(n2)、O(n2) | O ( 1 ) O(1) O(1) | 稳定 | 适用于元素少且基本有序的情况,适用于顺序和链式 |
折半插入 | O ( n log n ) 、 O ( n 2 ) 、 O ( n 2 ) O(n\\log n)、O(n^2)、O(n^2) O(nlogn)、O(n2)、O(n2) | O ( 1 ) O(1) O(1) | 稳定 | 适用于
n
n
n较大、无序的情况,只能用于顺序结构 |
希尔排序 |
O
(
n
1.5
)
O(n^{1.5})
O(n1.5)(平均) | O ( 1 ) O(1) O(1) | 不稳定 | 适用于
n
n
n较大、无序的情况,只能用于顺序结构 |
冒泡排序 | O ( n ) 、 O ( n 2 ) 、 O ( n 2 ) O(n)、O(n^2)、O(n^2) O(n)、O(n2)、O(n2) | O ( 1 ) O(1) O(1) | 稳定 | 适用于元素少且基本有序的情况,适用于顺序和链式 |
快速排序 | O ( n log n ) 、 O ( n 2 ) 、 O ( n log n ) O(n\\log n)、O(n^2)、O(n\\log n) O(nlogn)、O(n2)、O(nlogn) | O ( log n ) O(\\log n) O(logn) | 不稳定 | 平均时间性能最好 适用于
n
n
n较大、无序的情况,只能用于顺序结构 |
简单选择 | O ( n 2 ) 、 O ( n 2 ) 、 O ( n 2 ) O(n^2)、O(n^2)、O(n^2) O(n2)、O(n2)、O(n2) | O ( 1 ) O(1) O(1) | 稳定 | 移动次数少且每一记录占用较多空间时,比直插快 |
堆排序 | O ( n log n ) 、 O ( n log n ) 、 O ( n log n ) O(n\\log n)、O(n\\log n)、O(n\\log n) O(nlogn)、O(nlogn)、O(nlogn) | O ( 1 ) O(1) O(1) | 不稳定 | 记录少时不宜采用,只能用于顺序结构 |
归并排序 | O ( n log n ) 、 O ( n log n ) 、 O ( n log n ) O(n\\log n)、O(n\\log n)、O(n\\log n) O(nlogn)、O(nlogn)、O(nlogn) | O ( 1 ) O(1) O(1) | 稳定 | |
基数排序 | O ( d ( n + r d ) ) 、 O ( d ( n + r d ) ) 、 O ( d ( n + r d ) ) O(d(n+rd))、O(d(n+rd))、O(d(n+rd)) O(d(n+rd))、O(d(n+rd))、O(d(n+js 常用的比较排序算法总结 |