Java-优先级队列(堆)和堆排序实现
一,优先级队列的概念
优先级队列中,数据按关键词有序排列,插入新数据的时候,会自动插入到合适的位置保证队列有序。
举个例子来说,一组整型数,如果使用优先级队列的话,不管队列之前放入的数据如何,后面添加进去的数据总会被按照升序或者降序排列,优先队列的头是基于自然排序或Comparator排序的最小元素。
如果有多个对象拥有同样的排序,那么就可能随机地取其中任意一个。当我们获取队列时,返回队列的头对象。
二,优先级队列的方法
1,优先级队列是比栈和队列更专用的结构,在多数情况下都非常有用。优先级队列像普通队列一样,有一个队头和队尾,并且也是从队头移除数据
2,优先级队列中的元素可以按照任意顺序插入, 却总是按照排序的顺序检索;也就是说, 无论何时调用 remove方法, 总会获得当前优先队列中最小的元素
最大堆基本操作
public class MaxHeap{
//存取元素动态数组
private ArrayList<Integer> data;
public MaxHeap (){
data = new ArrayList<>();
}
public MaxHeap(int capacity){
data = new ArrayList<>(capacity);
}
//添加元素
public void add(int val){
//1,向数组末尾添加元素
data.add(val);
//2,调整当前堆的结构,使其仍满足最大堆
siftUp(data.size() - 1);
}
/**
* 取出当前堆的最大值
* 继续调整堆
*/
public int extractMax() {
if (isEmpty()) {
System.err.println("heap is empty!");
return -1;
}
int max = data.get(0);
// 将最后一个元素顶到堆顶
int lastVal = data.get(data.size() - 1);
data.set(0,lastVal);
// 移除最后一个元素
data.remove(data.size() - 1);
// siftDown
siftDown(0);
return max;
}
//堆化heapify
public MaxHeap(int[] arr){
data = new ArrayList<>(arr.length);
}
public boolean isEmpty(){
return data.size() == 0;
}
//获取父节点索引
private int parent(int k){
return (k - 1)/2;
}
private int left(int k){
return 2 * k + 1;
}private int right(int k){
return 2 * k + 2;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0;i < data.size();i++) {
sb.append(data.get(i));
if(i != data.size() - 1) {
sb.append(",");
}
}
sb.append("]");
return sb.toString();
}
}
元素上浮操作
/**
* 元素上浮操作
* @param i 上浮元素索引
*/
private void siftUp(int i) {
while (i > 0 && (data.get(i) > data.get(parent(i)))){
swap(i,parent(i));
}
//继续向上判断交换后父节点向上的节点关系
i = parent(i);
}
//交换当前数组i和parent的值
private void swap(int i, int parent) {
int temp = data.get(i);
int parentVal = data.get(parent);
data.set(i,parentVal);
data.set(parent,temp);
}
元素下浮操作
private void siftDown(int i) {
while (left(i) < data.size()) {
int j = left(i);
// 取出左右孩子的最大值
if (right(i) < data.size()) {
int leftVal = data.get(left(i));
int rightVal = data.get(right(i));
if (leftVal < rightVal) {
j = j + 1;
}
}
// j一定是左右孩子最大值的索引
// data[i] 和 data[j]
if (data.get(i) >= data.get(j)) {
// 当前节点已经大于左右孩子,下沉结束
break;
}else {
// swap(i,j);
swap(i,j);
i = j;
}
}
}