在之前的文章当中,我们通过海盗分金币问题详细讲解了递归方法。

我们可以认为在递归的过程当中,我们通过函数自己调用自己,将大问题转化成了小问题,因此简化了编码以及建模。今天这篇文章呢,就正式和大家聊一聊将大问题简化成小问题的分治算法的经典使用场景——排序

排序算法

排序算法有很多,很多博文都有总结,号称有十大经典的排序算法。我们信手拈来就可以说上来很多,比如插入排序、选择排序、桶排序、希尔排序、快速排序、归并排序等等。老实讲这么多排序算法,但我们实际工作中并不会用到那么多,凡是高级语言都有自带的排序工具,我们直接调用就好。为了应付面试以及提升自己算法能力呢,用到的也就那么几种。今天我们来介绍一下利用分治思想实现的两种经典排序算法——归并排序与快速排序

归并排序

我们先来讲归并排序,归并排序的思路其实很简单,说白了只有一句话:两个有序数组归并的复杂度是 O(n)

我们举个例子:

a = [1, 4, 6]b = [2, 4, 5]c = []

我们用i和j分别表示a和b两个数组的下标,c表示归并之后的数组,显然一开始的时候i, j = 0, 0。我们不停地比较a和b数组i和j位置大小关系,将小的那个数填入c。

填入一个数之后:

i = 1j = 0a = [1, 4, 6]b = [2, 4, 5]c = [1]

填入两个数之后:

i = 1j = 1a = [1, 4, 6]b = [2, 4, 5]c = [1, 2]

我们重复以上步骤,直到a和b数组当中所有的数都填入c数组为止,我们可以很方便地写出以上操作的代码:

从上面的代码我们也能看出来,这个过程虽然简单,但是写成代码非常麻烦,因为我们需要判断数组是否已经全部填入的情况。这里有一个简化代码的优化,就是在a和b两个数组当中插入一个”标兵“,这个标兵设置成正无穷大的数,这样当a数组当中其他元素都弹出之后。由于标兵大于b数组当中除了标兵之外其他所有的数,就可以保证a数组永远不会越界,如此就可以简化很多代码了(前提,a和b数组当中不存在和标兵一样大的数)。

我们来看代码:

这里应该都没有问题,接下来的问题是我们怎么利用归并数组的操作来排序呢?

其实很简单,这也是归并排序的精髓

我们每次将一个数组一分为二,显然,这个划分出来的数组不一定是有序的。但如果我们继续切分呢?直到数组当中只有一个元素的时候,是不是就天然有序了呢?

我们举个例子:

          [4, 1, 3, 2]           /                [4, 1]    [3, 2]        /        /          [4]   [1]  [3]   [2]            /        /        [1, 4]     [2, 3]                   /          [1, 2, 3, 4]

通过上面的这个过程我们可以发现,在归并排序的时候,我们先一直往下递归切分数组,直到所有的切片当中只有一个元素天然有序。接着一层一层地归并回来,当所有元素归并结束的时候,数组就完成了排序。这也就是归并排序的全部过程。

如果还不理解,还可以参考一下下面的动图。

我们来试着用代码来实现。之前我曾经在面试的时候被要求在白板上写过归并排序,当时我用的C++觉得编码还有一定的难度。现在,当我用习惯了Python之后,我感觉编码难度降低了很多。因为Python支持许多数组相关的高级操作,比如切片,变长等等。整个归并排序的代码不超过20行,我们一起来看下代码:

你看,无论是思想还是代码实现,归并排序并不难,就算一开始不熟悉,写个两遍也一定没问题了。

理解了归并排序之后,再来学快速排序就不难了,我们一起来看快速排序的算法原理。

快速排序

快速排序同样利用了分治的思想,我们每次做一个小的操作,让数组的一部分变得有序,之后我们通过递归,将这些有序的部分组合在一起,达到整体有序

在归并排序当中,我们划分问题的方法是横向切分,我们直接将数组一分为二,针对这两个部分分别排序。快排稍稍不同,它并不是针对数组的横向切分,而是从问题本身出发的”纵向“切分。在快速排序当中,我们解决的子问题不是对数组的一部分排序,而是提升数组的有序程度。怎么提升呢?

我们在数组当中寻找一个数,作为标杆,我们利用这个标杆调整数组当中元素的顺序。将小于它的放到它的左侧,大于它的放到它的右侧。这么一个操作结束之后,可以肯定的是,这个标杆所在的位置就是排序完成之后,它应该在的位置

我们来看个例子:

a = [8, 4, 3, 9, 10, 2, 7]

我们选择7作为标杆,一轮操作之后可以得到:

a = [2, 4, 3, 7, 9, 10, 8]

接着我们怎么做呢?很简单,我们只需要对标杆前面以及标杆后面的部分递归调用重复上述操作即可。如果还不明白的同学可以看一下下面这张动图:

如果用C++写过快排的同学肯定对于快排的代码印象深刻,它是属于典型的原理不难,但是写起来很麻烦的算法。因为快速排序需要用到两个下标,写的时候一不小心很容易写出bug。同样,由于Python当中动态数组的支持非常好,我们可以避免使用下标来实现快排,这样代码的可读性以及编码难度都要降低很多。

多说无益,我们来看代码:

整个代码除去注释,不到15行,我想大家应该都非常容易理解。

最后,我们来分析一下这两个算法的复杂度,为什么说这两个算法都是 O(nlogn) 的算法呢?(不考虑快速排序最差情况)这个证明非常简单,我们放一张图大家一看就明白了:

我们在递归的过程当中,我们只遍历了一遍数组,虽然我们每一层都会讲数组拆分。但是在递归树上同一层的递归函数遍历的总数加起来应该是等于数组的总长也就是n的。

而且递归的层数是有限制的,因为我们每次都将数组一分为二。而一个数组的最小长度是1,也就是说极端情况下我们一共能有 log2n 层,每一层的复杂度总和是n,所以整体的复杂度是 nlogn。

当然对于快速排序算法来说,如果数组是倒序的,我们默认取最后一个元素作为标杆的话,我们是无法切分数组的,因为除它之外所有的元素都比它大。在这种情况下算法的复杂度会退化到 n^2 。所以我们说快速排序算法最差复杂度是 n^2 。

到这里,关于归并排序与快速排序的算法就讲完了。这两个算法并不难,我想学过算法和数据结构的同学应该都有印象,但是在实际面试当中,真正能把代码写出来并且没有明显bug的实在是不多。我想,不论之前是否已经学会了,回顾一下都是很有必要的吧。

今天的文章就到这里,希望大家有所收获。如果喜欢本文,请顺手点个关注吧。

排序中减治法算法伪代码_算法浅谈——分治算法与归并、快速排序(附代码和动图演示)...相关推荐

  1. 快速排序 c++_算法浅谈——分治算法与归并、快速排序(附代码和动图演示)

    本文始发于个人公众号:TechFlow 在之前的文章当中,我们通过海盗分金币问题详细讲解了递归方法. 我们可以认为在递归的过程当中,我们通过函数自己调用自己,将大问题转化成了小问题,因此简化了编码以及 ...

  2. 排序中减治法算法伪代码_【算法与数据结构】伪代码与流程图

    js难的是抽象概念 编程语言也能抽象 JS Python java PHP 根本就没区别 这些语言都有 声名 if...else while 循环 for 循环 函数 对象 这意味着 你没有必要过分关 ...

  3. 十大经典排序算法Python版实现(附动图演示)

    来源:大数据DT 本文约5200字,建议阅读10分钟 排序算法是<数据结构与算法>中最基本的算法之一.本文介绍10种常见的内部排序算法,及如何用Python实现. 排序算法可以分为内部排序 ...

  4. 一文读懂Python版的十大经典排序算法(附动图演示)

    来源:大数据DT 本文约5200字,建议阅读10分钟 排序算法是<数据结构与算法>中最基本的算法之一.本文介绍10种常见的内部排序算法,及如何用Python实现. 排序算法可以分为内部排序 ...

  5. 算法 - 十大经典排序算法(动图演示)

    [TOC] 算法 - 十大经典排序算法(动图演示) ​ 在计算机科学与数学中,一个排序算法(英语:Sorting algorithm)是一种能将一串资料依照特定排序方式进行排列的一种算法.最常用到的排 ...

  6. 数据结构与算法--经典10大排序算法(动图演示)【建议收藏】

    十大经典排序算法总结(动图演示) 算法分类 十大常见排序算法可分为两大类: 比较排序算法:通过比较来决定元素的位置,由于时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序 非比较类型排 ...

  7. 十大经典排序算法(动图演示,收藏好文)

     0.算法概述  0.1 算法分类 十种常见排序算法可以分为两大类: 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序. 线 ...

  8. c 语言从大到小排序算法,10 大经典排序算法(动图演示+ C 语言代码)

    原标题:10 大经典排序算法(动图演示+ C 语言代码) 来源:C语言与CPP编程 以前也零零碎碎发过一些排序算法,但排版都不太好,又重新整理一次,排序算法是数据结构的重要部分,系统地学习很有必要. ...

  9. 程序员面试必备:动图演示十大经典排序算法及代码实现

    0.算法概述 0.1 算法分类 十种常见排序算法可以分为两大类: 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序. 非比较类排序: ...

最新文章

  1. lin通讯从节点同步间隔场_低成本总线技术——LIN总线协议规范介绍
  2. php实现单选和多选功能,input:checkbox多选框实现单选效果跟radio一样
  3. unef螺纹_小螺纹大学问,11种螺纹类型,你都使用过吗,了解它的使用方法吗
  4. 流操作中的noException
  5. linux 软件_RansomExx勒索软件现在也针对Linux系统
  6. 用两种方法求解九宫算问题
  7. Swift - 炫酷放射弹出按钮菜单(改造自AwesomeMenu)
  8. java选择题库_java题库1-单项选择题.doc
  9. C语言自学之路九(用C语言编写小游戏-扫雷)
  10. HTML期末大作业~酒店网站模板(HTML+CSS+JavaScript)
  11. 南方cass简码识别大全_CASS分类简码
  12. Linux gre tunnel 端口,两台Linux通过GRE tunnel的隧道实现互通 — 并且改变其中一台的回程路由...
  13. nginx源码的安装与磁盘分区
  14. ArcGIS地图抽稀
  15. Linux高级命令进阶(week1_day2)--技术流ken
  16. 使用FTP协议的服务,包括匿名用户验证访问,用户验证访问,虚拟用户验证访问等
  17. Linux驱动编程篇(三)——LED驱动(一)简单LED驱动
  18. python实现【冒泡排序】(bubbleSort)
  19. Pytorch二维卷积 conv2d 使用/源码/手写实现conv2d/手写向量内积实现conv2d/转置卷积实现——学习笔记
  20. 用U盘在Mac OS X中安装Windows7系统

热门文章

  1. java 如何实现计数_如何高效的实现一个计数器map
  2. 关于 SAP 产品 UI 的搜索引擎优化 SEO - Search Engine Optimization
  3. TypeScript Downleveling - 什么是 TypeScript 的降级行为
  4. SAP UI5 的本地 Fiori sandbox Launchpad
  5. SAP Cloud for Customer Cloud(C4C)Application Studio里的代码调试
  6. SAP Spartacus B2B 页面的 Popup Component
  7. SAP S/4HANA 的30天免费试用版
  8. Angular ActivatedRouteSnapshot
  9. zone.js在bootstrap阶段对window对象里一些标准方法的注入逻辑
  10. 一个好用的Chrome扩展应用,Neater Bookmarks