文章目录
优先队列
优先队列的主要操作:
- 入队
- 出队(取出优先级最高的元素)
例子:在N个元素中选出前M个元素?(比如,在100万个元素中选出前100名)
排序,NlogN
优先队列,NlogM(维护一个容量为M的最小堆,遍历完100万个数组之后,形成的最小堆中的M个元素就是最小的)
总共N个请求,使用普通数组或者顺序数组,最差情况O(n^2)
使用堆,O(nlogn)
二叉堆
最大堆两个特点:
- 二叉树上任何一个节点都不大于其父节点,(并不能说明,层数越高,值就越大)
- 是一颗完全二叉树。除了最后一层外,其他层的节点数必须是最大值,最后一层,节点数虽然可以不是最大值,但是所有节点必须集中在左侧,
用数组存储二叉堆,
节点规律:
- 已知父节点i,则
left child(i) = 2*i, right child = 2 * i + 1
- 已知 子节点i,则父节点
parent(i) = i / 2
二叉堆中插入元素(ShiftUp)
步骤:
- 将元素插入二叉堆的最底层,
- ShiftUp调整,将插入元素与其父节点比较,一步步向上转换(转换规则:如果该节点比其父节点还大,就往上换)
二叉堆中弹出元素(ShiftDown)
步骤:
- 把根节点弹出来,把最底下的元素拿出来放入根节点,
- ShiftDown调整,将根节点元素一步步向下转换(转换规则:比较左右孩子的大小,如果根节点比最大的子孩子还大,则将其与最大的孩子换位置)
堆排序——复杂度O(nlogn)
可以借助二叉堆来实现,思路是:
- 将任意数组依次insert到最大堆O(logn)(也可以使用Heapify操作,将数组形成最大堆,复杂度O(n))
- 再从最大堆中依次弹出,就能得到排好序的降序序列;
(想要得到升序序列,利用最小堆即可)
Heapify(将数组构造成堆)——复杂度O(n)
这是一个将给定数组,形成最大堆的方法,
将n个元素逐个插入到一个空堆中,复杂度是O(nlogn),而heapify过程,复杂度只有O(n)。
具体操作是:
- 将元素顺序塞入最大堆中去,
- 对于所有的叶子节点,本身就是一个最大堆,不用进行处理;从第一个不是叶子节点的节点(从n/2的位置开始,逆序降到1) 开始, 逐级向上处理,将叶子节点的父节点当做一个最大堆,这时候只需执行ShiftDown操作,将小的根节点往下转换,一直递归到最底层结束。
【分析】:
将n个元素逐个插入到一个空堆中,算法复杂度是O(nlogn),
原地堆排序
步骤: 拿出最大堆顶端的最大值,与最后一个元素交换,最大元素已经归位,count–,拿出此时的顶端值,使用ShiftDown调整顶端值,使得剩余元素依然是一个最大堆,继续拿出顶端的最大值,与最后一个元素交换,使用ShiftDown调整,依次类推。
对于这个二叉堆的节点编号(根节点为0号),父节点与子节点的关系为:
parent(i) = (i -1) /2
left child(i) = 2 * i + 1
right chid(i) = 2 * i + 2
索引堆
在堆中存放的是索引,数组本身没有发生改变。比较大小的时候,比较的是data数据,但是构建索引堆的时候,堆中存放的是data数据对应的下标。元素交换的时候,也只是交换索引即可。
- 构建一个索引堆
- 实现索引堆的插入,弹出元素功能
- 实现元素交换的功能(将原数组中第i个位置的元素替换成一个新的值item):需要先找到这个元素在索引堆中的位置(需要遍历索引数组,复杂度O(n)),然后分别进行ShiftUp & ShiftDown
索引堆的改进——反向查找
构造reverse数组,reverse数组中存放的是数组的下标在indexes中的位置。
使之与indexes数组互为逆序,即indexes[i] = j , reverse[j] = i
indexes[reverse[i]]=i
reverse[i]本身的定义就是表示索引i在indexes中的位置,因此indexes[reverse[i]]=i
reverse[indexes[i]] = i
indexes[i]表示i这个位置的索引是谁,reverse表示这个索引在indexes中的位置,