package com.datastructure;
import java.util.*;
/**
* 堆(heap):一种满足特定条件的完全二叉树
* <p>
* 1.分两种类型:
* 小顶堆(min heap):任意节点的值<= 其子节点的值
* 大顶堆(max heap):任意节点的值>=其子节点的值
* 2.堆的特性:
* 最底层节点靠左填充,其他层的节点都被填满;
* 二叉树的根节点称为堆顶,将底层最靠右的节点称为堆底
* 对于大顶堆(小顶堆),堆顶元素(根节点)的值是最大(最小)的
* <p>
* 3.堆通常用于实现优先队列,大顶堆相当于元素按照从大到小的顺序出队的优先队列。
* <p>
* 4.堆常用操作:
* 方法名 描述 时间复杂度
* push() 元素入堆 O(logn)
* pop() 堆顶元素出堆 O(logn)
* peek() 访问堆顶元素(对于大/小顶堆分别为最大/小值) O(1)
* size() 获取堆的元素数量 O(1)
* isEmpty() 判断堆是否为空 O(1)
* <p>
* 5.堆的实现:底层存储使用数组实现;是自下而上的构建
* <p>
* 6.堆化(heapify):修复从插入节点到根节点的路径上的各个节点
*
* 7.堆的常见应用:
* 优先队列
* 堆排序
* 获取最大的k个元素 eg:选择热度前 10 的新闻作为微博热搜,选取销量前10的商品
*/
public class Heap1 {
public static void main(String[] args) {
/**
* 初始化堆
*/
//初始化小顶堆 (默认从小到大)
Queue<Integer> minHeap = new PriorityQueue<>();
//初始化大顶堆(使用lambda表达修改Comparator) (从大到小)
Queue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
/**
* 元素入堆
*/
maxHeap.offer(1);
maxHeap.offer(3);
maxHeap.offer(2);
maxHeap.offer(5);
maxHeap.offer(4);
/**
* 获取堆顶元素
*/
int peek = maxHeap.peek();
System.out.println("peek = " + peek);
/**
*堆顶元素出堆
*/
//出堆元素会形成一个从大到小的序列
peek = maxHeap.poll();
System.out.println("peek = " + peek);
peek = maxHeap.poll();
System.out.println("peek = " + peek);
peek = maxHeap.poll();
System.out.println("peek = " + peek);
peek = maxHeap.poll();
System.out.println("peek = " + peek);
peek = maxHeap.poll();
System.out.println("peek = " + peek);
/**
* 获取堆大小
*/
int size = maxHeap.size();
System.out.println("size = " + size);
/**
* 判断堆是否为空
*/
boolean isEmpty = maxHeap.isEmpty();
System.out.println("isEmpty = " + isEmpty);
/**
* 输入列表并建堆
*/
minHeap = new PriorityQueue<>(Arrays.asList(1, 3, 2, 5, 4));
System.out.println("minHeap = " + minHeap.poll());
}
}
class MyHeap {
/**
* 使用列表而非数组,这样无须考虑扩容问题
*/
private List<Integer> maxHeap;
/**
* 根据输入构建堆
*
* @param nums
*/
public MyHeap(List<Integer> nums) {
// 将列表元素原封不动添加进堆
maxHeap = new ArrayList<>(nums);
// 堆化除叶节点以外的其他所有节点
for (int i = parent(size() - 1); i >= 0; i--) {
siftDown(i);
}
}
/**
* 获取左子节点的索引
*/
public int left(int i) {
return 2 * i + 1;
}
/**
* 获取右子节点的索引
*/
public int right(int i) {
return 2 * i + 2;
}
/**
* 获取父节点的索引
*/
public int parent(int i) {
return (i - 1) / 2;//向下整除
}
/**
* 访问堆顶元素:即二叉树的根节点,也就是列表的首个元素
*
* @return
*/
public int peek() {
return maxHeap.get(0);
}
/**
* 交换元素
*/
private void swap(int i, int j) {
int tmp = maxHeap.get(i);
maxHeap.set(i, maxHeap.get(j));
maxHeap.set(j, tmp);
}
/**
* 获取堆大小
*/
public int size() {
return maxHeap.size();
}
/**
* 判断堆是否为空
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* 元素入堆
*/
public void push(int val) {
//添加节点
maxHeap.add(val);
// 从底至顶堆化
siftUp(size() - 1);
}
/**
* 从节点 i 开始,从底至顶堆化
*/
private void siftUp(int i) {
while (true){
//获取节点i的父节点
int p=parent(i);
//当“越过根节点”或”节点无须修复“时,结束堆化
if(p<0||maxHeap.get(i)<=maxHeap.get(p))
break;
//交换两个节点
swap(i,p);
//循环向上堆化
i=p;
}
}
/**
* 元素出堆
* @return
*/
public int pop(){
//判空处理
if(isEmpty()){
throw new IndexOutOfBoundsException();
}
//交换根节点与最右叶节点(交换首元素与尾元素)
swap(0,size()-1);
//删除节点
int val=maxHeap.remove(size()-1);
//从顶至底堆化
siftDown(0);
//返回堆顶元素
return val;
}
/**
* 从节点 i 开始,从顶至底堆化
*/
private void siftDown(int i) {
while (true){
//判断节点i,l,r中值最大的节点,记作ma
int l=left(i);
int r=right(i);
int ma=i;
if(l<size()&&maxHeap.get(l)>maxHeap.get(ma))
ma=l;
if(r<size()&&maxHeap.get(r)>maxHeap.get(ma))
ma=r;
if(ma==i)// 若节点 i 最大或索引 l, r 越界,则无须继续堆化,跳出
break;
//交换两节点
swap(i,ma);
//循环向下堆化
i=ma;
}
}
}
详解堆(heap)的实现
于 2024-01-30 16:22:17 首次发布
本文介绍了Java中的堆数据结构,包括小顶堆和大顶堆的概念、特性、操作方法(如push、pop、peek和size等),以及如何使用PriorityQueue和自定义MyHeap类实现堆。重点展示了堆在优先队列和堆排序中的应用。
摘要由CSDN通过智能技术生成