排序算法
下面算法均是使用Python实现:
插入排序
原理:在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。通常使用在长度较小的数组的情况以及作为其它复杂排序算法的一部分,比如mergesort或quicksort。时间复杂度为 O(n^2) 。
# 1nd: 两两交换
def insertion_sort(seq):
for i in range(1, len(seq)):
j = i
while j >= 0 and seq[j-1] > seq[j]:
seq[j], seq[j-1] = seq[j-1], seq[j]
j -= 1
return seq
# 2nd: 交换,最后处理没交换的
def insertion_sort2(seq):
for i in range(1, len(seq)):
j = i-1
key = seq[i]
while j >= 0 and seq[j] > key:
seq[j+1] = seq[j]
j -= 1
seq[j+1] = key
return seq
# 3nd: 加速版本,利用已经排好了序的进行二分查找
def insertion_sort3(seq):
for i in range(1, len(seq)):
key = seq[i]
# invariant: ``seq[:i]`` is sorted
# find the least `low' such that ``seq[low]`` is not less then `key'.
# Binary search in sorted sequence ``seq[low:up]``:
low, up = 0, i
while up > low:
middle = (low + up) // 2
if seq[middle] < key:
low = middle + 1
else:
up = middle
# insert key at position ``low``
seq[:] = seq[:low] + [key] + seq[low:i] + seq[i + 1:]
return seq
# 4nd: 原理同上,使用bisect,面试不推荐使用
import bisect
def insertion_sort4(seq):
for i in range(1, len(seq)):
bisect.insort(seq, seq.pop(i), 0, i) # 默认插在相同元素的左边
return seq
选择排序
原理:每一趟都选择当前后面最小的值和当前下标的值进行交换,适用在大型的数组,时间复杂度为 O(n^2)
# 1nd: for
def select_sort(seq):
for i in range(0, len(seq)):
mi = i
# 选择坐标i后的最小值
for j in range(i, len(seq)):
if seq[j] < seq[mi]:
mi = j
seq[mi], seq[i] = seq[i], seq[mi]
return seq
# 2nd: min
def select_sort2(seq):
for i, x in enumerate(seq):
mi = min(range(i, len(seq)), key=seq.__getitem__)
seq[i], seq[mi] = seq[mi], x
return seq
冒泡排序
原理:比较数组中两两相邻的数,如果前者大于后者,就进行交换,重复地走访过要排序的数列,达到将最小的值移动到最上面的目的,适用于小型数组,时间复杂度为O(n^2)
def bubble_sort(seq):
for i in range(len(seq)-1):
j = len(seq)-1
while j > i:
if seq[j] < seq[j-1]:
seq[j], seq[j-1] = seq[j-1], seq[j]
j -= 1
return seq
def bubble_sort2(seq):
for i in range(0, len(seq)):
for j in range(i + 1, len(seq)):
if seq[i] > seq[j]:
seq[i], seq[j] = seq[j], seq[i]
return seq
快速排序
原理:从数组中选择pivot,分成两个数组,一个是比pivot小,一个是比pivot大,最后将这两个数组和pivot进行合并,最好情况下时间复杂度为O(n log n),最差情况下时间复杂度为O(n^2)
def quick_sort(seq):
if len(seq) <= 1:
return seq
pivot_idx = len(seq) // 2 # 将中间作为基准
small, large = [], []
for idx, val in enumerate(seq):
if idx != pivot_idx:
if val <= seq[pivot_idx]:
small.append(val)
else:
large.append(val)
small = quick_sort(small)
large = quick_sort(large)
return small + [seq[pivot_idx]] + large
def quick_sort(seq, left, right):
"""
单纯移动下标的实现
:param seq:
:param left:
:param right:
:return:
"""
if left >= right:
return seq
# 选择参考点,该调整范围的第1个值
target = seq[left]
i, j = left, right
# 循环判断直到遍历全部
while i < j:
# 从右边开始查找大于参考点的值
while i < j and seq[j] >= target:
j -= 1
seq[i] = seq[j] # 这个位置的值先挪到左边
# 从左边开始查找小于参考点的值
while i < j and seq[i] <= target:
i += 1
seq[j] = seq[i] # 这个位置的值挪到右边
# 写回改成的值
seq[i] = target
# 递归,并返回结果
quick_sort(seq, left, i - 1) # 递归左边部分
quick_sort(seq, i + 1, right) # 递归右边部分
return seq
归并排序
原理:归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
# 1nd: 将两个有序数组合并到一个数组
def merge(left, right):
i, j = 0, 0
result = []
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result += left[i:]
result += right[j:]
return result
def merge_sort(seq):
if len(seq) <= 1:
return seq
mid = len(seq) // 2
left = merge_sort(seq[:mid])
right = merge_sort(seq[mid:])
return merge(left, right)
# 2nd: use merge,面试不推荐使用
from heapq import merge
def merge_sort2(m):
if len(m) <= 1:
return m
middle = len(m) // 2
left = m[:middle]
right = m[middle:]
left = merge_sort(left)
right = merge_sort(right)
return list(merge(left, right))
堆排序
原理:堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值。一般升序采用大顶堆,降序采用小顶堆。平均时间复杂度为O(n logn)。
def heapify(seq, start, end):
"""
找出从start到end的范围内的最大值,放在堆顶的位置
:param seq:
:param start:
:param end:
:return:
"""
# start结点的左右子结点
left, right = 2 * start + 1, 2 * (start + 1)
ma = start
# 选出最大值
if left < end and seq[start] < seq[left]:
ma = left
if right < end and seq[ma] < seq[right]:
ma = right
if ma != start:
# 找到最大值后调整位置
seq[start], seq[ma] = seq[ma], seq[start]
heapify(seq, ma, end)
def heap_sort(seq):
start, end = len(seq) // 2 - 1, len(seq)
# 创建堆,i从根结点开始调整
for i in range(start, -1, -1):
heapify(seq, i, end)
# 调整堆,将堆顶元素与末尾元素进行交换,使末尾元素最大。
# 然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换
for i in range(end - 1, 0, -1):
seq[i], seq[0] = seq[0], seq[i]
heapify(seq, 0, i)
return seq
# 2nd: use heapq
import heapq
def heap_sort2(seq):
""" Implementation of heap sort """
heapq.heapify(seq)
return [heapq.heappop(seq) for _ in range(len(seq))]
希尔排序
原理:希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
def shell_sort(seq):
count = len(seq)
step = 2
group = count // step
while group > 0:
for i in range(0, group):
j = i + group
while j < count:
k = j - group
key = seq[j]
while k >= 0:
if seq[k] > key:
seq[k + group] = seq[k]
seq[k] = key
k -= group
j += group
group //= step
return seq
区别
转载于: