算法导论读书笔记(未完成)
堆排序
总览
我想了想,还是结合书中的每一句话来解释一下他们的来龙去脉吧,首先堆排序的时间复杂度是O(nlgn),也就是我们所说的大O时间复杂度。为什么说是nlgn的时间复杂度呢?首先我们需要知道max-heapfify的时间复杂度是lgn,也就是我们通常所说的log2n,因为数据结构当中的堆本质上还二叉树。那么这个n从哪里来呢?每一次Max-heapify都是一个调整堆成为最大堆的过程,每一次只能调整一个元素,因为堆中有n个元素,所以我们要调整n次才能把所有元素调整到正确的地方,这很像冒泡排序,但是实际上这并不是冒泡排序。稍后会跟大家详细说一下。
那么说到堆排序,我们首先应该去思考一下,什么是堆?既然堆是二叉树,那么堆肯定会满足二叉树相关的性质。另外这里所说的堆,并不是Java的JVM所说的堆,这里所说的堆,仅仅限于数据结构。如果用数组来进行表示的话,需要注意一点,堆中的有效元素,并不是整个数组,它有可能会小于这个数组的元素个数,那么为什么会是这样的呢?具体可以参考这篇文章。
堆当中有3个比较重要的概念,一个是根节点,一个是左子树,一个是右子树。而堆一般分为最大堆和最小堆,一般来说:堆排序所说的,都是最大堆,而最小堆一般用于构造优先队列。那么下面我们还是拿最大堆来说一下,最大堆,顾名思义,根节点是最大的,它应该比左子树和右子树的节点值都要大,这不仅仅说是比它的孩子大,而且这个操作是递归的。也就是说没有任何一个节点能够比根节点更大。那么左子树和右子树之间有没有什么关系呢?如果用数组来表示的话,我觉得只是一个物理存储位置之间的关系。二叉树的遍历相关的知识点大家可以去百度上搜一下,永远记住这么一点,二叉树的完美之处是在于它利用了计算机的原理,也就是二进制移位,这样可以大大提高效率,这就是为什么计算机只有2叉树,没有三叉树,四叉树的原因了。在最大堆当中,根节点的值永远大于或等于子节点的值。
维护堆的性质
这一节来看看怎么维护堆的正确性。先来看一段伪代码吧,下面这段代码是不能独立的,它依赖于i的位置。也就是说,i其实是根节点(我们所认为的),其实它在树当中不一定是根节点,还有一点想跟大家阐述的是:根节点是相对的,相对于它的子树来说,只要是非叶子节点,都可以认为是根节点。而下面的代码,实际上只是根据最大堆的性质,把大的往上移而已;注意最后一句话,它是一个递归,也就是说,它的作用是把大的节点尽可能地往根上面靠拢,直到不能再交换位置为止。递归的终止条件就是largets==i.
建堆
当然也就是创建一个堆了,本质上来说,建堆其实也就是一个不断调整元素的过程,使其达到最大堆,因为如果一个数组是未排序的,如果想按照堆排序来进行排序,那么就要利用下面的代码。注意一下,为什么是a.lenght/2呢?还是回到之前提到的二叉堆的性质,MAX-HEAPIFY是向下进行遍历的,而A.lenght/2恰好是叶子节点的父节点。这样做的好处,就是可以把每一个节点都放到正确的位置上,就像这样来解释:曾祖父,爷爷,父亲,你,儿子,孙子。这几个的顺序是乱的,假设先从儿子开始,儿子发现他的位置比孙子小(也就是儿子是子节点,孙子是父节点),然后交换顺序,第一次循环结束,第二次发现你居然比儿子的顺序要小,然后交换你和儿子的顺序,继续查找,发现孙子居然排在你的前面了,赶紧交换位置,然后你和儿子和孙子都在正确的位置上了。这么不停循环,直到曾祖父,大家最后都在了正确的位置上,所以建堆的时间复杂度是外层循环n和内层MAX-HEAPIFY的一个乘积。
快速排序
![](https://pic.cnblogs.com/avatar/567748/20200111202800.png)
出处:http://www.cnblogs.com/kmsfan
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
欢迎大家加入KMSFan之家,以及访问我的优酷空间!