堆和优先级队列
目录
一.二叉树的顺序存储方式
1.存储方式
2.下标关系
已知双亲(parent)的下标,则:
左孩子(left)下标 = 2 * parent + 1;
右孩子(right)下标 = 2 * parent + 2;
已知孩子(不区分左右)(child)下标,则:双亲(parent)下标 = (child - 1) / 2;
二.堆
1.概念
- 堆逻辑上是一棵完全二叉树
- 堆物理上是保存在数组中
- 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆
反之,则是小堆,或者小根堆,或者最小堆
4.堆的基本作用是,快速找集合中的最值
2.向下调整
<1>思路
前提:左右子树必须已经是一个堆,才能调整。
注:
- array 代表存储堆的数组
- size 代表数组中被视为堆数据的个数
- index 代表要调整位置的下标
思路:
注意的小问题:
1.判断index位置是不是叶子
<1>. 判断 index 位置有没有孩子
<2> 因为堆是完全二叉树,没有左孩子就一定没有右孩子,所以判断是否有左孩子
<3> 因为堆的存储结构是数组,所以判断是否有左孩子即判断左孩子下标是否越界,即 left >= size 越界
2.如何找最小的孩子
<1> 如果右孩子不存在,则 min = left
<2> 否则,比较 array[left] 和 array[right] 值得大小,选择小的为 min
<2>代码示例
//向下调整
public static void AdjustDown(Integer []array,int size,int index ){
while (true) {
int leftindex=2*index+1;
//index是叶子结点,直接return
if(leftindex>=size){
return;
}
//找最小的孩子
int minIndex=leftindex;
int rightIndex=leftindex+1;
//存在右孩子,且右孩子的值比左孩子的小,将最小孩子小标改为右孩子
if(rightIndex<size&&array[rightIndex]<array[leftindex]){
minIndex=rightIndex;
}
//3.比较最小孩子的值和index位置的值
if(array[index]<=array[minIndex]){
return;
}
//4.交换
int t=array[index];
array[index]=array[minIndex];
array[minIndex]=t;
//5.把最小的孩子视为index,继续循环
index=minIndex;
}
}
3.向上调整
<1>思路
1.判断index是不是树的根,如果是根调整结束
2.找到index的父节点
3.比较父节点的值和index的值
4.只要父节点的值<=index的值,调整结束
5.否则交换父节点和index的值
6.把父节点看作index,继续循环
<2>代码示例
//向上调整
public static void AdjustUp(Integer []array,int size,int index){
//1.判断index是不是树的根,如果是根调整结束
//2.找到index的父节点
//3.比较父节点的值和index的值
//4.只要父节点的值<=index的值,调整结束
//5.否则交换父节点和index的值
//6.把父节点看作index,继续循环
while (true) {
if(index==0){
break;
}
//找到index父节点的下标
int ParentIndex=(index-1)/2;
if(array[index]<array[ParentIndex]){
int t=array[index];
array[index]=array[ParentIndex];
array[ParentIndex]=t;
}else {
break;
}
index=ParentIndex;
}
}
3.建堆
<1>思路
1.找到层序遍历最后一个结点的下标
2.找到最后一个结点的父节点下标(最后一个不是叶子节点的下标),记位ParentIndex
3.从[lastParentIndex,0]不断地向下调整(从后往前)
<2>代码示例
//最坏时间复杂度是O(log(n))
//建堆
public static void BuildHeap(Integer array[],int size){
//找到层序遍历最后一个结点的下标
int lastIndex=size-1;
//找到最后一个结点的父节点下标(最后一个不是叶子节点的下标)
int lastParentIndex=(lastIndex-1)/2;//下标是从0开始
//从[lastParentIndex,0]不断地向下调整(从后往前)
for (int i=lastParentIndex;i>=0;i--){
AdjustDown(array, size, i);
}
}
三.优先级队列
1.概念
返回最高优先级对象,添加新的对象。这种数据结构就是优先级队列(Priority Queue)
2.优先级队列的实现
使用堆来构建
代码示例:返回队首元素,入队,出队
需要使用上述提到的:堆的向下和向上调整
public class MyPriorityQueue {
private Integer[] array;
private int size;
public MyPriorityQueue() {
//简单起见,不考虑扩容
array=new Integer[100];
size=0;
}
//返回队首元素
public Integer element(){
if(size==0){
throw new RuntimeException("空了" );
}
return array[0];
}
//返回并删除队首元素
public Integer remove(){
if(size==0){
throw new RuntimeException("空了" );
}
int e=array[0];
//将堆的最后一个元素拿到第一位(堆首),此时满足向下调整的条件
array[0]=array[size-1];
//将堆首(0号下标)进行向下调整
Heap.AdjustDown(array, size, 0);
return e;
}
//插入一个元素
//时间复杂度O(log(n))
public void add(Integer e){
array[size]=e;
size++;
Heap.AdjustUp(array, size, size-1);
}
}
四.TopK问题
举例(Top5问题)