上期为大家讲解了排序算法常见的几个概念:
如果有遗忘的同学可以看排序算法——(1)简介这篇文章复习一下。
今天将为大家介绍常用的十大排序算法中最简单的五种(冒泡、选择、插入、希尔、归并),主要从:过程图解、算法思想、代码实现、算法分析这四个方面讲解,建议大家看完之后自己动手练习加强记忆!
注:本文使用的复杂度均为最坏复杂度
冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的元素列,依次比较两个相邻的元素,一层一层的将较大的元素往后移动,其现象和气泡在上升过程中慢慢变大类似,故成为冒泡排序。
def bubble_sort(arr):
"""冒泡排序"""
# 第一层for表示循环的遍数
for i in range(len(arr) - 1):
# 第二层for表示具体比较哪两个元素
for j in range(len(arr) - 1 - i):
if arr[j] > arr[j + 1]:
# 如果前面的大于后面的,则交换这两个元素的位置
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
冒泡排序是一种简单直接暴力的排序算法,为什么说它暴力?因为每一轮比较可能多个元素移动位置,而元素位置的互换是需要消耗资源的,所以这是一种偏慢的排序算法,仅适用于对于含有较少元素的数列进行排序。
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,所以称为:选择排序
def selection_sort(arr):
"""选择排序"""
# 第一层for表示循环选择的遍数
for i in range(len(arr) - 1):
# 将起始元素设为最小元素
min_index = i
# 第二层for表示最小元素和后面的元素逐个比较
for j in range(i + 1, len(arr)):
if arr[j] < arr[min_index]:
# 如果当前元素比最小元素小,则把当前元素角标记为最小元素角标
min_index = j
# 查找一遍后将最小元素与起始元素互换
arr[min_index], arr[i] = arr[i], arr[min_index]
return arr
选择排序和冒泡排序很类似,但是选择排序每轮比较只会有一次交换,而冒泡排序会有多次交换,交换次数比冒泡排序少,就减少cpu的消耗,所以在数据量小的时候可以用选择排序,实际适用的场合非常少。
插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
def insertion_sort(arr):
"""插入排序"""
# 第一层for表示循环插入的遍数
for i in range(1, len(arr)):
# 设置当前需要插入的元素
current = arr[i]
# 与当前元素比较的比较元素
pre_index = i - 1
while pre_index >= 0 and arr[pre_index] > current:
# 当比较元素大于当前元素则把比较元素后移
arr[pre_index + 1] = arr[pre_index]
# 往前选择下一个比较元素
pre_index -= 1
# 当比较元素小于当前元素,则将当前元素插入在 其后面
arr[pre_index + 1] = current
return arr
插入排序的适用场景:一个新元素需要插入到一组已经是有序的数组中,或者是一组基本有序的数组排序。
希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量(间隔)排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本,它与插入排序的不同之处在于,它会优先比较距离较远的元素,该方法因D.L.Shell于1959年提出而得名。
希尔排序的整体思想是将固定间隔的几个元素之间排序,然后再缩小这个间隔。这样到最后数列就成为了基本有序数列,而前面我们讲过插入排序对基本有序数列排序效果较好。
已知的最增量式是由 Sedgewick 提出的 (1, 5, 19, 41, 109,…),该步长的项来自 9 * 4^i - 9 * 2^i + 1 和 4^i - 3 * 2^i + 1 这两个算式。这项研究也表明 "比较在希尔排序中是最主要的操作,而不是交换。 用这样增量式的希尔排序比插入排序和堆排序都要快,甚至在小数组中比快速排序还快,但是在涉及大量数据时希尔排序还是比快速排序慢。
def shell_sort(arr):
"""希尔排序"""
# 取整计算增量(间隔)值
gap = len(arr) // 2
while gap > 0:
# 从增量值开始遍历比较
for i in range(gap, len(arr)):
j = i
current = arr[i]
# 元素与他同列的前面的每个元素比较,如果比前面的小则互换
while j - gap >= 0 and current < arr[j - gap]:
arr[j] = arr[j - gap]
j -= gap
arr[j] = current
# 缩小增量(间隔)值
gap //= 2
return arr
归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序适用于子序列有序的数据排序。
从上图看分解后的数列很像一个二叉树。
def merge_sort(arr):
"""归并排序"""
if len(arr) == 1:
return arr
# 使用二分法将数列分两个
mid = len(arr) // 2
left = arr[:mid]
right = arr[mid:]
# 使用递归运算
return marge(merge_sort(left), merge_sort(right))
def marge(left, right):
"""排序合并两个数列"""
result = []
# 两个数列都有值
while len(left) > 0 and len(right) > 0:
# 左右两个数列第一个最小放前面
if left[0] <= right[0]:
result.append(left.pop(0))
else:
result.append(right.pop(0))
# 只有一个数列中还有值,直接添加
result += left
result += right
return result
今天给大家介绍的五种排序是比较简单的排序,建议大家自己动手敲几遍代码,书读百遍,其义自现。要求大家必须理解&记住它们的算法原理,因为代码是永远记不住的,只要记住原理你就能用伪代码实现。 为了方便大家记忆我在每个算法分析最后给出了自己的记忆方法,如果你有不理解的地方,欢迎在下方留言,同时也欢迎大家转发分享!