目录
前言
一、堆是什么?
![](https://i-blog.csdnimg.cn/blog_migrate/2ddfd989937b35940a1e4337fecb0d9a.png)
1.1堆的储存方式
二、堆的创建
代码如下:
public class TestHeap {
//给一个数组
private int [] elem;
//记录当前堆中 有效的数据个数
public int usedSize;
//构造方法
TestHeap(){
this.elem = new int[10];
}
//初始化elem数组
public void initElem(int [] array){
for (int i = 0; i < array.length; i++) {
elem[i] = array[i];
usedSize++;
}
}
//创建大根堆
public void createHeap(){
for (int parent = (usedSize-1-1)/2;parent >=0 ;parent--){
siftDown(parent,usedSize);
}
}
2.1:我们用向下调整的方法构建堆:
首先,siftDown
方法接受两个参数:parent
表示要调整的父节点的下标,len
表示堆数组的长度。在这个方法中,我们定义了一个 child
变量,表示左孩子的下标,初始值为 2*parent+1
。
接下来,使用一个循环来进行调整,只要左孩子的下标小于堆数组的长度,就继续进行调整。在循环中,首先进行的是判断右孩子是否存在并且比左孩子的值大,如果成立,就更新 child
为右孩子的下标。
接着,我们比较 elem[child]
和 elem[parent]
的值,如果左右孩子的最大值大于父节点的值,就进行交换,并更新 parent
和 child
的值,使其向下移动到子节点位置。交换操作使用了 swap
方法,用于交换数组中两个元素的值。
最后,如果左右孩子的最大值小于等于父节点的值,则跳出循环,结束调整。
/*
向下调整
*/
private void siftDown(int parent, int len){
int child = 2*parent+1;
//至少有左孩子
while (child < len ){
//左孩子 和 右孩子 比较大小 如果右孩子的值大 那么
if(elem[child] < elem[child+1]){
if(child+1 < len && elem[child] < elem[child]+1){
child =child+1;
}
}
//走完上述if语句 证明child下标 一定保存的是左右孩子最大值的下标
if (elem[child] > elem[parent]) {
//交换
swap(child,parent);
parent = child;
child = 2*parent +1;
}
//如果child<parent
else {
break;
}
}
}
private void swap(int i , int j){
int tmp = elem[i];
elem[i] = elem[j];
elem[j] = tmp;
}
2.2:建堆的时间复杂度
![](https://i-blog.csdnimg.cn/blog_migrate/1e4f4e907b06a0507148b8a07f19a151.png)
三:堆的方法
3.1:堆的插入
首先,push
方法接受一个参数 val
,表示要插入的值。在方法中,首先判断堆是否已满,如果满了,则将数组 elem
扩容为原来的两倍大小,使用 Arrays.copyOf
方法进行扩容操作。
然后,将要插入的值 val
存储在堆数组的 usedSize
位置上,并将 usedSize
自增。
接下来,调用 siftUp
方法进行向上调整,以保持堆的性质。在 siftUp
方法中,定义了一个 parent
变量,表示子节点的父节点下标,初始值为 (child - 1) / 2
。
然后,使用一个循环进行调整,只要子节点的下标大于0,就继续进行调整。在循环中,首先比较子节点和父节点的值,如果子节点的值大于父节点的值,就进行交换,并更新 child
和 parent
的值,使其向上移动到父节点位置。交换操作使用了 swap
方法,用于交换数组中两个元素的值。
最后,如果子节点的值小于等于父节点的值,则跳出循环,结束调整。
public void push(int val){
//满了
if(isFull()){
elem = Arrays.copyOf(elem,2*elem.length);
}
elem[usedSize] = val;
//向上调整
siftUp(usedSize);
usedSize++;
}
public boolean isFull(){
return usedSize == elem.length;
}
//向上调整
public void siftUp(int child) {
int parent = (child - 1) / 2;
while (child > 0) {
if (elem[child] > elem[parent]) {
swap(child, parent);
child = parent;
parent = (child - 1) / 2;
} else {
break;
}
}
}
3.2:堆的删除
堆的删除,我们要保证调整后它也是一个大根堆。
首先,pop
方法用于弹出堆顶元素。在方法中,首先判断堆是否为空,如果是,则返回 -1 表示操作失败。
然后,将堆顶元素 elem[0]
的值保存到变量 oldVal
中。接着,将堆顶元素和最后一个元素进行交换,并将 usedSize
自减,相当于删除了堆顶元素。然后,调用 siftDown
方法进行向下调整,以保持堆的性质。
最后,返回被弹出的堆顶元素的值 oldVal
。
empty
方法用于判断堆是否为空,如果 usedSize
的值为 0,则表示堆为空。
//判断是否为空
public int pop(){
if(empty()){
return -1;
}
int oldVal = elem[0];
swap(0,usedSize-1);
usedSize--;
siftDown(0,usedSize);
return oldVal;
}
public boolean empty(){
return usedSize == 0;
}
四:用堆模拟实现优先级队列
首先我们使用数组存储元素,并提供了 offer
、poll
和 peek
方法分别用于插入元素、弹出队列头部元素和查看队列头部元素。
在 offer
方法中,首先将要插入的元素存储在数组的末尾(下标为 size
),然后调用 shiftUp
方法进行向上调整,以保持堆的性质。在调用 shiftUp
方法时,传入的参数是数组末尾元素的下标 size - 1
。
在 poll
方法中,首先将队列头部元素保存到变量 oldValue
中,然后将数组末尾元素移动到队列头部,并将队列大小 size
自减。接着,调用 shiftDown
方法进行向下调整,以保持堆的性质。
在 peek
方法中,直接返回数组的第一个元素(即队列头部元素)。
public class MyPriorityQueue { private int[] array = new int[100]; private int size = 0; public void offer(int e) { array[size++] = e; shiftUp(size - 1); } public int poll() { int oldValue = array[0]; array[0] = array[--size]; shiftDown(0); return oldValue; } public int peek() { return array[0]; } }
总结
希望大家多多支持。给个三连吧