堆的定义
堆(Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵完全二叉树的数组对象。
这里涉及到一个概念,即完全二叉树。
完全二叉树是二叉树的一种特殊的形式,下面是来自百度百科对完全二叉树的释义:一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。
这里面又涉及到满二叉树的定义:满二叉树就是二叉树每一层的结点个数都达到了最大值, 即第i层上有2^i-1 个结点
下面给出我自己对完全二叉树的理解:
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。如下图所示就是一颗完全二叉树,图中树的深度为4,除了第4层,其他三层都达到了最大的节点数,第4层的节点也在最左边,所以它是一颗完全二叉树。
堆的类型
根据堆的性质之一——堆中某个节点的值总是不大于或不小于其父节点的值,可以将堆分为大根堆(最大堆)与小根堆(最小堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
注:堆的根节点中存放的是最大或者最小元素,但是其他节点的排序顺序是未知的。
堆的基本操作
- 上浮 shift_up
- 下沉 shift_down
- 插入 push
- 弹出 pop
- 取顶 top
- 堆排序 heap_sort
堆的算法思想
假设现在有一个小根堆,并且根的元素名为root ,其左右子节点为left和right ,此时插入一个新的元素,这种情况下,有两种可能:
(1)root<=left 并且root<=right,即根节点比它的左右子节点都小,则说明此时就是最小堆;
(2)root>left或者root>right ,此时root应与两个左右子节点中值较小的一个交换,结果得到一个堆,这个时候,如果root的值仍然大于其新的左右节点的一个或全部的两个的值,则重复上一步操作,继续和新的左右子节点中较小的值交换,即之前提到的堆的基本操作中的下沉操作(如果是最大堆则情况相反,相应的就是上浮操作)。重复这个过程,直至到达某一个层使它小于它所有子节点,或者它成了叶子结点。
堆的应用
根据前面堆的性质,我们知道堆的根节点存放的一组数据中最大或者最小的元素。所以堆经常用来解决TOPK问题。比如LeetCode中的 前 K 个高频元素,数组中的第K个最大元素等。
还有比如:如何从100万个数中找出最大的前100个数。我们可以先取出前100个数,对这100个数构建一个最小堆。然后遍历一遍剩余的元素,在此过程中维护这个最小堆就可以了。
步骤:
- 取前m个元素(例如m=100),建立一个小顶堆
- 顺序读取后续元素,直到结束。每次读取一个元素,如果该元素比堆顶元素小,直接丢弃
如果大于堆顶元素,则用该元素替换堆顶元素,然后保持最小堆性质。 - 最后这个堆中的元素就是前最大的100个。