升序用大顶堆还是小顶堆,堆排序默认大顶堆还是小顶堆

http://www.itjxue.com  2023-01-09 02:21  来源:未知  点击次数: 

PHP优先队列、二叉堆、大顶堆、小顶堆

先进后出(FILO),就像一个敞口向上的容器,只能将后进入容器的先弹出。

先进先出(FIFO),跟栈相反,队列就像一根上下贯通的水管,只能将先流入水管的水流出去。

优先队列也是一种数据结构,通过加权值进行排序,PHP核心库提供了 SplPriorityQueue 对象来实现。

优先队列内部是用 Heap:堆 这种数据结构来实现的,默认是大顶堆(MaxHeap)。

优先队列改成小顶堆,需要重写compare方法,将比较值对调,即可切换小顶堆和大顶堆。

堆就是为了实现优先队列而设计的一种数据结构,它分为大顶堆和小顶堆,PHP核心库提供了 大顶堆SplMaxHeap 和 小顶堆SplMinHeap 两种类可供直接使用,他们都是由SplHeap抽象类实现的。

总结:

堆排序为什么一定要将堆调整成最大堆或者最小堆

建堆是自底向上的且序列位于无序状态,此时除了要选取堆顶元素以外还要保证所有子树的根与左右结点之间符合堆的标准(根是三个结点中取值最小的(小顶堆,降序)/最大的(大顶堆,升序))。

堆调整是自顶向下的序列处于基本有序状态。此时只需要关注自顶向下移动路径上的各个分支是否在交换后依然符合堆的标准。

两个过程有明显差别,自然时间复杂度不一样了。

谈谈堆排序,大顶堆,小顶堆?

堆是一种非线性结构, 可以 把堆看作一个数组, 也可以 被看作一个完全二叉树,通俗来讲堆其实就是 利用完全二叉树的结构来维护的一维数组 但堆并不一定是完全二叉树

按照堆的特点可以把堆分为大顶堆和小顶堆

大顶堆:每个结点的值都大于或等于其左右孩子结点的值

小顶堆:每个结点的值都小于或等于其左右孩子结点的值

如果仅仅是需要得到一个有序的序列,使用排序就可以很快完成,并不需要去组织一个新的数据结构。但是如果我们的需求是对于一个随时会有更新的序列,我要随时知道这个序列的最小值或最大值是什么。显然如果是线性结构,每次插入之后,假设原数组是有序的,那使用二分把它放在正确的位置也未尝不可,但是插入的时候从数组中留出空位就需要O(n)的时间复杂度,删除的时候亦然。

可是如果我们将序列看作是一个集合,我们需要的是这个集合的一个最小值或者最大值,并且,在它被任意划分成为若干个子集的时候,这些子集的最小值或者最大值我们也是知道的,这些子集被不断划分,我们依然知道这些再次被划分出来的子集的最小值或者最大值。而且我们去想办法去保持这样的一个性质,那么这个问题是不是变得非常好解决了呢?那么问题就转换成了一种集合之间的关系,并且是非常明显的一种包含关系,那么最适合于解决这种集合上的关系的数据结构是什么呢?那么就是树,所以就形成了这样的一种树,他的每一个节点都比它的子节点们小或者大。 当我们插入一个新的节点的时候,实际上我们需要去调整的大部分时候只是这棵树上的一条路径,也就是决定它在哪一个集合里面,树上的路径长度相对于这个集合,由于是对数级别的,所以非常可以接受,那么这种数据结构也就应运而生,而这个数据结构为什么叫做堆,那就不知道了。

内存占用:

普通树占用的内存空间比它们存储的数据要多。你必须为节点对象以及左/右子节点指针分配额外的内存。 堆仅仅使用数组,且不使用指针

平衡:

二叉搜索树必须是“平衡”的情况下,其大部分操作的复杂度才能达到 O(nlog2n) 。你可以按任意顺序位置插入/删除数据,或者使用 AVL 树或者红黑树,但是在堆中实际上不需要整棵树都是有序的。我们只需要满足对属性即可,所以在堆中平衡不是问题。因为堆中数据的组织方式可以保证 O(nlog2n) 的性能

搜索:

在二叉树中搜索会很快,但是在堆中搜索会很慢。在堆中搜索不是第一优先级,因为 使用堆的目的是将最大(或者最小)的节点放在最前面,从而快速的进行相关插入、删除操作

堆排序的基本思想 假设是构建大顶堆

1.给一个无序序列如下

int a[6] = {7, 3, 8, 5, 1, 2}

2.根据数组将完全二叉树还原出来

3.为什么升序要用大顶堆呢

大顶堆的特点:每个结点的值都大于或等于其左右孩子结点的值,我们把大顶堆构建完毕后根节点的值一定是最大的,然后 把根节点和最后一个元素(也可以说最后一个节点)交换位置 ,那么末尾元素此时就是最大元素了

4.图解交换过程

先要找到最后一个非叶子节点,数组的长度为6,那么最后一个非叶子节点就是: 长度/2-1 ,也就是 6/2-1=2 ,然后下一步就是比较该节点值和它的子树值,如果该节点小于其左\右子树的值就交换(意思就是将最大的值放到该节点)

8只有一个左子树,左子树的值为2,82不需要调整

到这里大顶堆的构建就算完成了

然后下一步交换根节点 8 与最后一个元素 2 交换位置(将最大元素"沉"到数组末端),此时最大的元素就归位了,然后对剩下的5个元素重复上面的操作

这里用紫色来表示已经归位的元素

剩下只有5个元素,最后一个非叶子节点是 5/2-1=1 ,该节点的值 5 大于左子树的值 3 也大于右子树的值 1 ,满足大顶堆性质不需要交换

将堆排序的过程分成了两部分,构建一个大顶堆,就沉下去最大值,然后断开与最大值的链接,重新构建大顶堆

可以看到过程打印的数组值就是图解中的每步。

堆排序的最坏、最好、平均时间复杂度均为 O(nlogn) ,是不稳定排序算法。

参考文章

选择排序和堆排序

最近了解到了hadoop中的merge过程,其中使用到了外部排序。

因此总结一下选择排序和堆排序:

选择排序 :选择排序的时间复杂度为O(n^2);不需要额外的存储空间。也就是简单选择排序。

就是每次选出来一个最小值,和第一个元素交换;第二轮再选未排序队列中的最小值。。。

'''

'''

树形选择排序 的优点是,相较于简单选择排序,是和中间结果比较,减少了比较的次数。树形选择排序只有叶子节点是待排序列,中间结点是排序的中间结果。

外部排序中:多路归并排序+败者树(用到了内存中的归并排序(分治法,divide and conquer)+外部的多路归并排序,多路归并的限制因素是外部磁盘的访问次数,虽然多路减少了磁盘的读写次数,但是多路的极值比较增多,抵消掉多路归并中磁盘读写带来的性能提升,因此采用败者树)

败者树:是一种完全二叉树,是 树形选择排序 的变种。 只有叶子节点是待排序的。中间节点是排序的中间结果。

选择排序(O(n^2))--树形选择排序(额外的存储空间较大)-- 堆排序

堆排序: 所有的节点都是待比较的数据 。

大顶堆和小顶堆:

每个节点都大于或者等于左右孩子结点的值,称为大顶堆;

每个节点都小于或者等于左右孩子节点的值,成为小顶堆;

并且,堆是完全二叉树,因此可以用数组来进行存储。

以前学这些,因为没有具体的应用场景,不是很理解,每次看完就忘。

(责任编辑:IT教学网)

更多

推荐软件水平考试文章