排序算法——(2)Python实现十大经常使用排序算法

上期为你们讲解了排序算法常见的几个概念:html

  1. 相关性:排序时是否须要比较元素
  2. 稳定性:相同元素排序后是否可能打乱
  3. 时间空间复杂度:随着元素增长时间和空间随之变化的函数

若是有遗忘的同窗能够看排序算法——(1)简介这篇文章复习一下。python

今天将为你们介绍经常使用的十大排序算法中最简单的五种(冒泡、选择、插入、希尔、归并),主要从:过程图解、算法思想、代码实现、算法分析这四个方面讲解,建议你们看完以后本身动手练习增强记忆!
排序一览表
注:本文使用的复杂度均为最坏复杂度算法

1、冒泡排序

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的元素列,依次比较两个相邻的元素,一层一层的将较大的元素日后移动,其现象和睦泡在上升过程当中慢慢变大相似,故成为冒泡排序。shell

1.过程图解

冒泡排序

2.算法思想

  1. 从第一个和第二个开始比较,若是第一个比第二个大,则交换位置,而后比较第二个和第三个,逐渐日后
  2. 通过第一轮后最大的元素已经排在最后,因此重复上述操做的话第二大的则会排在倒数第二的位置。
  3. 那重复上述操做n-1次便可完成排序,由于最后一次只有一个元素因此不须要比较

3.代码实现

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 

4.算法分析

冒泡排序是一种简单直接暴力的排序算法,为何说它暴力?由于每一轮比较可能多个元素移动位置,而元素位置的互换是须要消耗资源的,因此这是一种偏慢的排序算法,仅适用于对于含有较少元素的数列进行排序。数组

  1. 稳定性:咱们从代码中能够看出只有前一个元素大于后一个元素才可能交换位置,因此相同元素的相对顺序不可能改变,因此它是稳定排序
  2. 比较性:由于排序时元素之间须要比较,因此是比较排序
  3. 时间复杂度:由于它须要双层循环n*(n-1)),因此平均时间复杂度为O(n^2)
  4. 空间复杂度:只须要常数个辅助单元,因此空间复杂度为O(1),咱们把空间复杂度为O(1)的排序成为原地排序(in-place)
  5. 记忆方法:想象成气泡,一层一层的往上变大

2、选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工做原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,因此称为:选择排序app

1.过程图解

选择排序

2.算法思想

  1. 设第一个元素为比较元素,依次和后面的元素比较,比较完全部元素找到最小的元素,将它和第一个元素互换
  2. 重复上述操做,咱们找出第二小的元素和第二个位置的元素互换,以此类推找出剩余最小元素将它换到前面,即完成排序

3.代码实现

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 

4.算法分析

选择排序和冒泡排序很相似,可是选择排序每轮比较只会有一次交换,而冒泡排序会有屡次交换,交换次数比冒泡排序少,就减小cpu的消耗,因此在数据量小的时候能够用选择排序,实际适用的场合很是少。ide

  1. 比较性:由于排序时元素之间须要比较,因此是比较排序
  2. 稳定性:由于存在任意位置的两个元素交换,好比[5, 8, 5, 2],第一个5会和2交换位置,因此改变了两个5原来的相对顺序,因此为不稳定排序。
  3. 时间复杂度:咱们看到选择排序一样是双层循环n*(n-1)),因此时间复杂度也为:O(n^2)
  4. 空间复杂度:只须要常数个辅助单元,因此空间复杂度也为O(1)
  5. 记忆方法:选择对象要先选最小的,由于嫩,哈哈

3、插入排序

插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工做原理是经过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。函数

1.过程图解

插入排序

2.算法思想

  1. 从第二个元素开始和前面的元素进行比较,若是前面的元素比当前元素大,则将前面元素 后移,当前元素依次往前,直到找到比它小或等于它的元素插入在其后面
  2. 而后选择第三个元素,重复上述操做,进行插入
  3. 依次选择到最后一个元素,插入后即完成全部排序

3.代码实现

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 

4.算法分析

插入排序的适用场景:一个新元素须要插入到一组已是有序的数组中,或者是一组基本有序的数组排序。ui

  1. 比较性:排序时元素之间须要比较,因此为比较排序
  2. 稳定性:从代码咱们能够看出只有比较元素大于当前元素,比较元素才会日后移动,因此相同元素是不会改变相对顺序
  3. 时间复杂度:插入排序一样须要两次循坏一个一个比较,故时间复杂度也为O(n^2)
  4. 空间复杂度:只须要常数个辅助单元,因此空间复杂度也为O(1)
  5. 记忆方法:想象成在书架中插书:先找到相应位置,将后面的书日后推,再将书插入

4、希尔排序

希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量(间隔)排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本,它与插入排序的不一样之处在于,它会优先比较距离较远的元素,该方法因D.L.Shell于1959年提出而得名。spa

1.过程图解

希尔排序

2.算法思想

希尔排序的总体思想是将固定间隔的几个元素之间排序,而后再缩小这个间隔。这样到最后数列就成为了基本有序数列,而前面咱们讲过插入排序对基本有序数列排序效果较好。

  1. 计算一个增量(间隔)值
  2. 对元素进行增量元素进行比较,好比增量值为7,那么就对0,7,14,21…个元素进行插入排序
  3. 而后对1,8,15…进行排序,依次递增进行排序
  4. 全部元素排序完后,缩小增量好比为3,而后又重复上述第2,3步
  5. 最后缩小增量至1时,数列已经基本有序,最后一遍普通插入便可

已知的最增量式是由 Sedgewick 提出的 (1, 5, 19, 41, 109,…),该步长的项来自 9 * 4^i - 9 * 2^i + 1 和 4^i - 3 * 2^i + 1 这两个算式。这项研究也代表 "比较在希尔排序中是最主要的操做,而不是交换。 用这样增量式的希尔排序比插入排序和堆排序都要快,甚至在小数组中比快速排序还快,可是在涉及大量数据时希尔排序仍是比快速排序慢。

3.代码实现

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 

4.算法分析

  1. 比较性:排序时元素之间须要比较,因此为比较排序
  2. 稳定性:由于希尔排序是间隔的插入,因此存在相同元素相对顺序被打乱,因此是不稳定排序
  3. 时间复杂度: 最坏时间复杂度O(n2)平均复杂度为O(n1.3)
  4. 空间复杂度:只须要常数个辅助单元,因此空间复杂度也为O(1)
  5. 记忆方法:插入排序是每轮都是一小步,希尔排序是先大步后小步,它第一个突破O(n2)的排序算法。联想起阿姆斯特朗登月以后说:这是我我的一小步,倒是人类迈出的一大步。

5、归并排序

归并排序(MERGE-SORT)是创建在归并操做上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个很是典型的应用。归并排序适用于子序列有序的数据排序。

1.过程图解

归并排序

2.算法思想

从上图看分解后的数列很像一个二叉树。

  1. 使用递归将源数列使用二分法分红多个子列
  2. 申请空间将两个子列排序合并而后返回
  3. 将全部子列一步一步合并最后完成排序

3.代码实现

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 

4.算法分析

  1. 比较性:排序时元素之间须要比较,因此为比较排序
  2. 稳定性:咱们从代码中能够看到当左边的元素小于等于右边的元素就把左边的排前面,而本来左边的就是在前面,因此相同元素的相对顺序不变,故为稳定排序
  3. 时间复杂度: 复杂度为O(nlog^n)
  4. 空间复杂度:在合并子列时须要申请临时空间,并且空间大小随数列的大小而变化,因此空间复杂度为O(n)
  5. 记忆方法:所谓归并确定是要先分解,再合并

总结

今天给你们介绍的五种排序是比较简单的排序,建议你们本身动手敲几遍代码,书读百遍,其义自现。要求你们必须理解&记住它们的算法原理,由于代码是永远记不住的,只要记住原理你就能用伪代码实现。 为了方便你们记忆我在每一个算法分析最后给出了本身的记忆方法,若是你有不理解的地方,欢迎在下方留言,同时也欢迎你们转发分享!

相关文章
相关标签/搜索