文章目录
优先队列
什么是优先队列
普通队列: 先进先出, 后进后出
优先队列: 出队顺序和入队顺序无关, 和优先级相关
堆的基本结构
- 二叉堆是一棵二叉树
- 二叉堆是一棵完全二叉树( 完全二叉树就是把元素一层一层的排列, 直到排不下为止)
- 堆的某个节点值总是不大于其父节点的值(最大堆)
使用数组存储堆
因为堆是完全二叉树, 所以可以把堆一层一层的放入数组. 使用数组实现二叉结构
此时计算节点如下:
- parent(i) = i/2;
- left child(i) = i * 2;
- right child(i) = i * 2 + 1;
堆的使用操作
向堆中添加元素和shiftUp
shiftUp, 就是将一个元素, 上浮到符合堆性质的位置.
取出堆的最大元素和shiftDown
shiftDown, 和shiftUp类似, 就是把一个元素下浮到指定位置
replace 替换操作
replace: 取出最大元素后, 放入一个新元素
实现: 可以先extractMax, 再add, 两次O(logn)的操作
实现: 可以直接将堆顶元素替换以后shifWown, 一次O(logn)的操作
heapify 操作
heapify: 将任意数组整理成堆的形状
实现: 从第一个非子元素的地方, 上浮元素。
堆的实现代码
package pers.jssd.heap;
/**
* 最大堆
*
* @author jssdjing@gmail.com
*/
public class MaxHeap<E extends Comparable<E>> {
private Array<E> data;
public MaxHeap(int capacity) {
data = new Array<>(capacity);
}
public MaxHeap() {
data = new Array<>();
}
public MaxHeap(E[] arr) {
data = new Array<E>(arr);
for (int i = getParent(data.getSize() - 1); i >= 0; i--) {
shiftDown(i);
}
}
/**
* 取得堆中所含元素的大小
*
* @return 返回堆中元素的多少
*/
public int getSize() {
return data.getSize();
}
/**
* 判断堆是否为空
*
* @return true则为空, false则不为空
*/
public boolean isEmpty() {
return data.isEmpty();
}
/**
* 取得父元素的位置
*
* @param i 子孩子的位置
* @return 返回此子孩子的父元素的位置
*/
private int getParent(int i) {
if (i == 0) {
throw new IllegalArgumentException("can not get parent, the heap is empty");
}
return (i - 1) / 2;
}
/**
* 取得左孩子
*
* @param i 父元素的位置
* @return 返回左孩子的位置
*/
private int getLeftChild(int i) {
return i * 2 + 1;
}
/**
* 取得右孩子
*
* @param i 父元素位置
* @return 返回右孩子的位置
*/
private int getRightChild(int i) {
return i * 2 + 2;
}
/**
* 添加元素
*
* @param e 要添加的元素
*/
public void addData(E e) {
data.addLast(e);
shiftUp(data.getSize() - 1);
}
/**
* 将k位置的元素上浮到指定位置
*
* @param k 上浮的元素位置
*/
private void shiftUp(int k) {
while (k > 0 && data.get(k).compareTo(data.get(getParent(k))) > 0) {
data.swap(k, getParent(k));
k = getParent(k);
}
}
/**
* 取得最大元素
*
* @return 返回取得的最大元素
*/
public E getMax() {
if (data.isEmpty()) {
throw new IllegalArgumentException("can not get max value when the heap is empty");
}
return data.get(0);
}
/**
* 取出最大元素. 并删除
*
* @return 返回取出的最大元素
*/
public E extractMax() {
E ref = getMax();
data.swap(0, data.getSize() - 1);
data.removeLast();
shiftDown(0);
return ref;
}
/**
* 下浮元素
*
* @param k 将k位置的元素下浮到指定位置
*/
private void shiftDown(int k) {
while (getLeftChild(k) < data.getSize()) {
int i = getLeftChild(k);
if (i + 1 < data.getSize() && data.get(i).compareTo(data.get(i + 1)) < 0) {
i = i + 1;
}
if (data.get(k).compareTo(data.get(i)) >= 0) {
break;
}
data.swap(k, i);
k = i;
}
}
/**
* 将最大元素取出, 并添加一个元素
*
* @param e 将要替换的元素
* @return 返回被替换的元素
*/
public E replace(E e) {
E ref = getMax();
data.set(0, e);
shiftDown(0);
return ref;
}
}
优先队列的实现(使用堆)
package pers.jssd.priorityqueue;
import pers.jssd.heap.MaxHeap;
/**
* @author jssdjing@gmail.com
*/
public class PriorityQueue<E extends Comparable<E>> implements Queue<E> {
private MaxHeap<E> heap;
public PriorityQueue() {
heap = new MaxHeap<>();
}
@Override
public void enqueue(E e) {
heap.addData(e);
}
@Override
public E dequeue() {
return heap.extractMax();
}
@Override
public E getFront() {
return heap.getMax();
}
@Override
public int getSize() {
return heap.getSize();
}
@Override
public boolean isEmpty() {
return heap.isEmpty();
}
}
leetCode例题
第347号问题:https://leetcode-cn.com/problems/top-k-frequent-elements/ 前k个高频元素
package pers.jssd.leetcode;
import pers.jssd.heap.MaxHeap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 第347号问题, 前k个高频元素
*/
class Solution {
/**
* 一个内部类, 含有元素和元素频率.
*/
private class Fre implements Comparable<Fre> {
int e;
int fre;
public Fre(int e, int fre) {
this.e = e;
this.fre = fre;
}
@Override
public int compareTo(Fre another) {
return Integer.compare(another.fre, this.fre);
}
}
public List<Integer> topKFrequent(int[] nums, int k) {
// 统计nums的数字频率到map中
Map<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
if (map.containsKey(num)) {
map.put(num, map.get(num) + 1);
} else {
map.put(num, 1);
}
}
System.out.println("map = " + map);
// 维护具有前k个元素的优先队列
MaxHeap<Fre> queue = new MaxHeap<>();
for (Integer integer : map.keySet()) {
if (queue.getSize() < k) {
queue.addData(new Fre(integer, map.get(integer)));
} else {
int maxValue = queue.getMax().e;
int max = queue.getMax().fre;
if (map.get(integer).compareTo(max) > 0) {
queue.replace(new Fre(integer, map.get(integer)));
}
}
}
// 将得到的前k个元素放入list中
List<Integer> list = new ArrayList<>();
int size = queue.getSize();
for (int i = 0; i < size; i++) {
list.add(0, queue.extractMax().e);
}
return list;
}
public static void main(String[] args) {
/*输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]*/
int[] nums = new int[]{-1, 1, 4, -4, 3, 5, 4, -2, 3, -1};
List<Integer> list = new Solution().topKFrequent(nums, 3);
for (Integer integer : list) {
System.out.println("integer = " + integer);
}
}
}
来源:https://blog.csdn.net/qq_36835560/article/details/99543159