最大堆:最大堆:堆中某个节点的值总是不大于其父节点的值,可以使用数组实现:按层,从上到下,从左到有,为节点序号i
往堆中增加元素的操作:往数组末尾增加,如果比父节点大,则交换,同理,一直到根节点;该操作称为siftUp
去除最大堆根元素的操作:将根元素去除,将最后一个元素放在根元素的位置,比较根元素左右子节点,如果左右子节点最大值大于根元素,则和最大的子节点交换
所以依次取出最大堆的根元素,对应的就是降序排列
完整代码如下:
/**
* 最大堆:堆中某个节点的值总是不大于其父节点的值
* 使用数组实现:按层,从上到下,从左到有,为节点序号i
* parent(i) = (i-1)/2
* left child(i) = i*2 + 1;
* right child(i) = i*2 + 2;
* @author zhoubw
*/
public class MaxHeap<E extends Comparable<E>> {
public static void main(String[] args) {
MaxHeap<Integer> maxHeap = new MaxHeap<>();
Integer[] nums = {2,1,5,3,4};
maxHeap.sortDesc(nums);
System.out.println(Arrays.toString(nums));
maxHeap.sortAsc(nums);
System.out.println(Arrays.toString(nums));
}
private ArrayList<E> data;
public MaxHeap() {
data = new ArrayList<E>();
}
public void add(E e) {
data.add(e);
siftUp(data.size()-1);
}
public E extractMax() {
if(data.isEmpty()) {
throw new RuntimeException("max heap is empty!");
}
E ret = data.get(0);
swap(0, data.size()-1);
data.remove(data.size()-1);
siftDown(0);
return ret;
}
/**
* 降序
* @param array
*/
public void sortDesc(E[] array) {
for(int i=0; i<array.length; i++) {
add(array[i]);
}
for(int i=0; i<array.length; i++) {
array[i] = extractMax();
}
}
/**
* 升序
* @param array
*/
public void sortAsc(E[] array) {
for(int i=0; i<array.length; i++) {
add(array[i]);
}
for(int i=0; i<array.length; i++) {
array[array.length-1-i] = extractMax();
}
}
/**
* index对应的元素比父节点大,则需要上浮操作
* @param index
*/
private void siftUp(int index) {
while(index>0) {
if(data.get(index).compareTo(data.get(parent(index))) < 0) {
break;
}
swap(index, parent(index));
index = parent(index);
continue;
}
}
/**
* index对应的元素比左或者右子节点小,则需要下沉操作
* @param index
*/
private void siftDown(int index) {
while(leftChild(index) < data.size()) {
int maxIndex = leftChild(index);
if(rightChild(index) < data.size()) {
maxIndex = data.get(leftChild(index)).compareTo(data.get(rightChild(index))) > 0 ? leftChild(index) : rightChild(index);
}
if(data.get(maxIndex).compareTo(data.get(index)) < 0) {
break;
}
swap(maxIndex, index);
index = maxIndex;
}
}
//获取index的父节点下标
private int parent(int index) {
if(index >0) {
return (index-1)/2;
}
return 0;
}
//获取index的左子节点的下标
private int leftChild(int index) {
return index*2+1;
}
//获取index的右子节点的下标
private int rightChild(int index) {
return index*2+2;
}
private void swap(int i, int j) {
E e1 = data.get(i);
data.set(i, data.get(j));
data.set(j, e1);
}
}