一.什么是堆
1.1:堆(Heaport)是数据结构的一种排序算法,满足其子节点总是大于or小于跟节点.
1.2:是一棵完全二叉树
例:
二.如何实现堆
2.1:我们以一个数组 arr[]={7,4,10,2,8,9,10,6,12,1};来讲解如何向下调整来创建一个堆
//初始化堆
public void initElem(int[] array) {
for (int i = 0; i < array.length; i++) {
elem[i] = array[i];
usedSize++;
}
}
2.2 用向下调整的方式来构建大根堆
1. 让 parent 标记需要调整的节点, child 标记 parent 的左孩子 ( 注意: parent 如果有孩子一定先是有左孩子 )2. 如果 parent 的左孩子存在,即 :child < size , 进行以下操作,直到 parent 的左孩子不存在parent 右孩子是否存在,存在找到左右孩子中最大的孩子,让 child 进行标将 parent 与大的孩子 child 比较,如果:parent大 于较大的孩子 child ,调整结束否则:交换 parent 与较大的孩子 child ,交换完成之后, parent 中小的元素向下移动,可能导致子树不满足对的性质,因此需要继续向下调整,即 parent = child ; child = parent*2+1; 然后继续 2 。
//向下调整构建大根堆堆
public void createMaxHead() {
for (int parent = (usedSize-1-1)/2; parent >=0; parent--) {
shiftDown(parent,usedSize);
}
}
//向下调整具体实现 perent为要调整的父节点 usedSize即范围
private void shiftDown(int parent, int usedSize) {
int child = 2*parent + 1;
//最起码 要有左孩子
while (child < usedSize) {
//一定是有右孩子的情况下
if(child+1 < usedSize && elem[child] < elem[child+1]) {
child++;
}
//child下标 一定是左右孩子 最大值的下标
if(elem[child] > elem[parent]) {
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
parent = child;
child = 2*parent+1;
}else {
break;
}
}
}
2.3:堆的插入
堆的插入总共需要两个步骤:
1.
先将元素放入到底层空间中
(
注意:空间不够时需要扩容
)
2.
将最后新插入的节点向上调整,直到满足堆的性质
//入堆 判断是否为空,如为空进行扩容
public void offer(int val) {
if(isFull()) {
elem = Arrays.copyOf(elem,2*elem.length);
elem[usedSize++] = val;
shiftUp(usedSize-1);
}
}
//向上调整 用于插入元素 将元素放到数组最后位置 然后与节点比较 若大于节点交换彼此 直至比到头节点
private void shiftUp(int child) {
int parent = (child-1)/2;
while (child > 0) {
if(elem[child] > elem[parent]) {
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
child = parent;
parent = (child-1)/2;
}else {
break;
}
}
}
2.4:堆的删除
1.
将堆顶元素对堆中最后一个元素交换
2.
将堆中有效数据个数减少一个
3.
对堆顶元素进行向下调整
![](https://img-blog.csdnimg.cn/095807cddd2542bb85a4bf30e9468bdf.png)
具体代码实现:
import java.util.Arrays;
public class MaxHeap {
private static int[] elem; //使用一个数组存储堆的元素
private static int usedSize; //存储元素的大小
public MaxHeap() {
this.elem = new int[10]; //指定堆的大小
this.usedSize = 0;
}
//初始化堆
public void initElem(int[] array) {
for (int i = 0; i < array.length; i++) {
elem[i] = array[i];
usedSize++;
}
}
//向下调整构建大根堆堆
public void createMaxHead() {
for (int parent = (usedSize-1-1)/2; parent >=0; parent--) {
shiftDown(parent,usedSize);
}
}
//向下调整具体实现 perent为要调整的父节点 usedSize即范围
private void shiftDown(int parent, int usedSize) {
int child = 2*parent + 1;
//最起码 要有左孩子
while (child < usedSize) {
//一定是有右孩子的情况下
if(child+1 < usedSize && elem[child] < elem[child+1]) {
child++;
}
//child下标 一定是左右孩子 最大值的下标
if(elem[child] > elem[parent]) {
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
parent = child;
child = 2*parent+1;
}else {
break;
}
}
}
//入堆 判断是否为空,如为空进行扩容
public void offer(int val) {
if(isFull()) {
elem = Arrays.copyOf(elem,2*elem.length);
elem[usedSize++] = val;
shiftUp(usedSize-1);
}
}
private boolean isFull() {
return elem.length==usedSize;
}
//向上调整 用于插入元素 将元素放到数组最后位置 然后与节点比较 若大于节点交换彼此 直至比到头节点
private void shiftUp(int child) {
int parent = (child-1)/2;
while (child > 0) {
if(elem[child] > elem[parent]) {
int tmp = elem[child];
elem[child] = elem[parent];
elem[parent] = tmp;
child = parent;
parent = (child-1)/2;
}else {
break;
}
}
}
//将要删除的头几点放到末尾 并重写向下调整
public int pop() {
if(isEmpty()) {
throw new RuntimeException("堆为空,删除操作无法完成!");
}
//交换头节点和尾节点
int tem =elem[usedSize-1];
elem[usedSize-1] = elem[0];
elem[0] = tem;
usedSize--;
shiftDown(0,usedSize);
return elem[0];
}
public int peek(){
return elem[0];
}
//判断是否为空
public static boolean isEmpty() {
return usedSize==0;
}
//打印堆
public static void show(){
for (int i = 0; i <usedSize; i++) {
System.out.print(elem[i]+" ");
}
System.out.println();
}
}
效果:
public class TestMaxHead {
public static void main(String[] args) {
MaxHeap m =new MaxHeap();
int[] arr={7,4,10,2,8,9,10,6,12,1};
m.initElem(arr);
System.out.println("将数组元素丢人堆中:");
m.show();
m.createMaxHead();
System.out.println("以大根堆的形式创建堆:");
m.show();
m.offer(666);
System.out.println("插入一个666");
m.show();
m.pop();
System.out.println("删除表头元素:");
m.show();
System.out.println("此时表头元素为:"+m.peek());
}
}
PriorityQueue的特性
Java
集合框架中提供了
PriorityQueue
和
PriorityBlockingQueue
两种类型的优先级队列,
PriorityQueue
是线
程不安全的,
PriorityBlockingQueue
是线程安全的
![](https://img-blog.csdnimg.cn/f7a0e2efa7b94265a31f4a314309166a.png)
1.
使用时必须导入
PriorityQueue
所在的包,即:
import java . util . PriorityQueue ;
2. PriorityQueue
中放置的
元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出
ClassCastException
异常
3.
不能
插入
null
对象,否则会抛出
NullPointerException
4.
没有容量限制,可以插入任意多个元素,其内部可以自动扩容
5.
插入和删除元素的时间复杂度为
6.
PriorityQueue
底层使用了堆数据结构
7.
PriorityQueue
默认情况下是小堆
---
即每次获取到的元素都是最小的元素
3.常用构造器
static void TestPriorityQueue(){
// 创建一个空的优先级队列,底层默认容量是11
PriorityQueue<Integer> q1 = new PriorityQueue<>();
// 创建一个空的优先级队列,底层的容量为initialCapacity
PriorityQueue<Integer> q2 = new PriorityQueue<>(100);
ArrayList<Integer> list = new ArrayList<>();
list.add(4);
list.add(3);
list.add(2);
list.add(1);
// 用ArrayList对象来构造一个优先级队列的对象
// q3中已经包含了三个元素
PriorityQueue<Integer> q3 = new PriorityQueue<>(list);
System.out.println(q3.size());
System.out.println(q3.peek());
}