排序算法 | 平均时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|
冒泡排序 | $O(n^2)$ | $O(1)$ | 稳定 |
选择排序 | $O(n^2)$ | $O(1)$ | 不稳定 |
插入排序 | $O(n^2)$ | $O(1)$ | 稳定 |
希尔排序 | $O(n\log^2 n)$ | $O(1)$ | 不稳定 |
归并排序 | $O(n\log^2 n)$ | $O(n)$ | 稳定 |
快速排序 | $O(n\log n)$ | $O(\log n)$ | 不稳定 |
堆排序 | $O(n\log n)$ | $O(1)$ | 不稳定 |
计数排序 | $O(n+k)$ | $O(k)$ | 稳定 |
基数排序 | $O(n\times k)$ | 稳定 | |
桶排序 | $O(n)$ | $O(m)$ | 稳定 |
插入排序
插入排序在对几乎已经排好序的数据操作时,效率高,可以达到线性排序的效率。
算法思路
插入排序的思路同我们打扑克抓牌码牌的思路非常相像,我们手中的牌是有序的,每次抓牌时,都将新抓的牌插入到手牌中,并使手中的牌保持有序。
若想保持这种有序状态,假设我们码牌时手中的牌是自左向右、由小到大排列的,每次新抓一张牌时都会将抓到的牌从最右边开始依次同手中的牌进行比较,若手中的牌大则继续向左比较,直至配到手中牌小于新抓牌的情况,将新抓牌插入到所比较的手中牌的后方,每次新抓到牌都持续这样的操作,完成码牌。
插入排序就是将待排序数列看做混乱的牌,按照码牌的规则将序列排序。
算法实现
在代码实现过程中,不能够凭空将一个数直接插入到数组中,所以在每次比较时,若已排序数列中进行比较的元素值大于待排序元素的值,则将此次比较的值后移一位为待排序值留出赋值空间,直至有序数列中进行比较的元素值小于或等于待排序元素值,将待排序元素值写入到之前空出的位置。
def insertion_sort(arr):
arr_len = len(arr)
if arr_len < 2:
return
for i in range(1, arr_len):
temp, j = arr[i], i-1
while j >= 0 and arr[j] > temp:
arr[j+1] = arr[j]
j -= 1
arr[j+1] = temp
优化算法—拆半插入
在寻找待插入位置时不再逐一比较,而是使用二分法进行比较。
def binary_insertion_sort(arr):
arr_len = len(arr)
if arr_len < 2:
return
for i in range(1, arr_len):
temp, start, end = arr[i], 0, i-1
while start <= end:
mid = (start + end) // 2
if temp > arr[mid]:
start = mid + 1
else:
end = mid - 1
j = i - 1
for _ in range(i-start):
arr[j+1] = arr[j]
arr[start] = temp
希尔排序
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本,不再局限于插入排序只能交换相邻元素的思路。
算法思路
希尔排序中,会将想相邻元素的比较交换改为间隔 h 的元素之间的交换,通过不断减小 h,使得序列逐渐趋向有序,当 h=1时,就是通过插入排序快速的将接近有序的序列排序。
算法实现
def shell_sort(arr):
arr_len, h = len(arr), 1
if arr_len < 2:
return
while h < arr_len / 3:
h = 3 * h + 1
while h >= 1:
for i in range(h, arr_len):
temp, j = arr[i], i-h
while j >= 0 and arr[j] > temp:
arr[j+h] = arr[j]
j -= h
arr[j+h] = temp
h //= 3
本博客文章仅供博主学习交流,博主才疏学浅,语言表达能力有限,对知识的理解、编写代码能力都有很多不足,希望各路大神多多包涵,多加指点。