1.为什么使用堆?
用数组保存二叉树,用层序遍历方式放在数组中。但只适合完全二叉树,因为非完全二叉树会有空间的浪费。
所以这种方式主要用法就是堆的表示
2.什么叫优先级对列
队列是现金先出的数据结构,但有时候操作的数据是带有优先级的,所以使用优先级队列。
3.优先级队列的两个基本操作
<1>.返回最高优先级对象.
<2>,添加新对象
4.优先级队列
//使用时必须导入PriorityQueue所在的包
/*
1.元素必须能比较大小
2.插入和删除元素的时间复杂度为O(log2N)
3.底层使用堆
*/
import java.util.PriorityQueue;
public static void main(String[] args) {
//定义整型的优先级队列:默认头部的元素为最小的元素
PriorityQueue<Integer> priorityQueue=new PriorityQueue<>();
//插入元素
priorityQueue.offer(13);
priorityQueue.offer(3);
priorityQueue.offer(8);
priorityQueue.offer(49);
//获取队列的头部————> 3
System.out.println(priorityQueue.peek());
//删除队列的头部————> 3
System.out.println(priorityQueue.poll());
//获取队列的头部————> 8
System.out.println(priorityQueue.peek());
}
}
5.模拟实现优先级队列——>堆
大堆:父亲结点比孩子结点都大
小堆:父亲结点比孩子结点都小
6.堆的作用
快速找集合中的最值
testHeap中的代码
package priorityqueue;
import java.util.Arrays;
public class TestHeap {
//堆的底层是一个数组
public int[] elem;
public int usedSize;
public TestHeap(){
this.elem=new int[10];
}
//像下调整
public void adjustDown(int parent,int len){
//左孩子
int child=2*parent+1;
//首先判断是否有左孩子
while(child<len){
//1.首先判断是否有右孩子,如果有,child保存左右孩子最大值下标
//左孩子和右孩子比较大小,但是child+1不能越界,
if(child+1<len && 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 initHeap(int[] array){
//将array数组的元素放在elem数组中
for (int i = 0; i < array.length; i++) {
this.elem[i]=array[i];
usedSize++;
}
//已知孩子结点求父亲结点则————>双亲的下标=(孩子下标—1)/2
for (int i = (usedSize-1-1)/2; i >=0; i--) {
adjustDown(i,usedSize);
}
System.out.println("===============");
}
//向上调整
public void adjustUp(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 void push(int val){
//如果满了放不下,先扩充数组
if(isFull()){
elem= Arrays.copyOf(elem,2*elem.length);
}
elem[usedSize]=val;
usedSize++;
//将新加的结点传进去
adjustUp(usedSize-1);
System.out.println("========");
}
//堆的删除:只能删除堆顶元素
/*
1.让堆顶元素和最后一个元素交换
*/
public void pop(){
//1.判空
if(isEmpty()) return;
//2.交换栈顶和最后一个元素
int tmp=elem[0];
elem[0]=elem[usedSize-1];
elem[usedSize-1]=tmp;
//删除最后一个元素
usedSize--;
//调整
adjustDown(0,usedSize);
}
public boolean isEmpty(){
return usedSize==0;
}
//当前数组的长度和使用长度一样,说明数组已经满了
public boolean isFull(){
return usedSize==elem.length;
}
//堆排序
public void heapSort(){
int end=usedSize-1;
while(end>0){
int tmp=elem[0];
elem[0]=elem[end];
elem[end]=tmp;
adjustDown(0,end);
end--;
}
}
public void show(){
for (int i = 0; i < usedSize; i++) {
System.out.print(elem[i]+" ");
}
System.out.println();
}
}
TestPriorityQueue的代码
import java.util.PriorityQueue;
public class TestPriorityQueue {
public static void main(String[] args) {
int[] array = {27, 15, 19, 18, 28, 34, 65, 49, 25, 37};
TestHeap testHeap = new TestHeap();
testHeap.initHeap(array);
System.out.println("================");
//testHeap.push(80);
testHeap.pop();
testHeap.heapSort();
testHeap.show();
}
}
7.堆的应用
例如求前K个最小的——>
大堆:每次弹出来的都是大元素,剩下的就是小的
例如求前K个最大的——>
小堆:每次弹出来的都是小元素,剩下的就是大的