介绍
是一种经过排序的完全二叉树,其中任一根节点的数据值均不大于其左子节点和右子节点的值。
则其root及根(堆顶)存储的就是整个堆中最小的值。
代码实现
/**
* 小顶堆
* 适用于在n个数中找最大的m个数字
*/
public class MinHeap {
/**
* 默认初始化长度
*/
private static final int DEFAULT_INITIAL_CAPACITY = 10;
/**
* 当前堆长度
*/
private int currentSize;
/**
* 堆的最大长度
*/
private int capacity;
/**
* 数据域
*/
private int[] queue;
/**
* 无参默认构造函数
*/
public MinHeap()
{
initHeap(DEFAULT_INITIAL_CAPACITY);
}
/**
* 有参构造
* @param capacity 容量
*/
public MinHeap(int capacity)
{
if(capacity<1||capacity>Integer.MAX_VALUE)
throw new IndexOutOfBoundsException("非法长度");
initHeap(capacity);
}
/**
* 初始化堆
* @param initCapacity 容量
*/
private void initHeap(int initCapacity)
{
this.capacity = initCapacity;
queue = new int[initCapacity];
currentSize = 0;
}
/**
* 添加一个元素
* @param x 数据
*/
public void add(int x)
{
//如果堆中没数据,则放到第一个
if(currentSize==0) {
queue[currentSize++] = x;
//如果位置合法就会直接加入
}else if(!isFull()) {
adjustUP(currentSize++,x);
//不合法会先进行判断然后
} else {
//如果栈顶比x小,则先移除,再再次尝试添加
if(x>getTop()) {
remove();
add(x);
}
}
}
/**
* 是否满堆
* @return 若满返回true
*/
public boolean isFull()
{
return currentSize>=capacity;
}
/**
* 获得堆顶元素
* @return
*/
public int getTop()
{
return queue[0];
}
/**
* 删除堆顶元素
* @return 堆顶
*/
public int remove()
{
//获得栈顶元素
int cur = getTop();
//获得最后一个元素
int temp = queue[--currentSize];
//将最后一个元素放到第一个
queue[0] = temp;
//进行调整
adjustDown(0,temp);
//返回栈顶元素
return cur;
}
/**
* 从下往上调整
* @param k 需要调整的位置
* @param value 需要调整的值
*/
private void adjustUP(int k,int value)
{
//直到调整到k<=0及调整到堆顶0号下标
while(k>0)
{
//获得父节点
int root = (k-1)>>>1;
//如果父节点的值小于等于要调整的值,说明符合小顶堆的要求,跳出
if(queue[root]<=value)
{
break;
}
//如果父节点大于要调整的值,则需要将父节点往下移
queue[k] = queue[root];
//k往上移动
k = root;
}
//将要调整的值放到k号位置
queue[k] = value;
}
/**
*从上往下调整
* @param k 调整的位置
* @param vale 调整的值
*/
private void adjustDown(int k,int vale)
{
//调整的界限
int half = currentSize>>>1;
while (k<half)
{
//获得左孩子id
int child = (k<<1)+1;
//获得左孩子的值
int childVal = queue[child];
//获得右孩子id
int RightChild = child+1;
//先判断是否存在右孩子,再判断左孩子值大还是右孩子值大:目的是找到两个孩子中最小的放到childVal
if(RightChild<currentSize && childVal>queue[RightChild])
{
childVal = queue[child = RightChild];
}
//如果孩子节点大于要调整的值,说明找到了正确位置,跳出
if(childVal>vale)
{
break;
}
//如果没找到将小的孩子节点放到根节点
queue[k] = queue[child];
//k往下移动
k = child;
}
//将值放入到k号位置
queue[k] = vale;
}
/**
* 打印当前的堆
*/
public void print()
{
for (int i = 0; i < currentSize; i++) {
System.out.print(queue[i]+" ");
}
System.out.println();
}
/**
* 找出数组中最大的capacityge个数字
* @param a 数组
*/
public void add(int[] a)
{
for (int i = 0; i < a.length; i++) {
add(a[i]);
}
}
}
实现难点
堆实现的难点主要在于其调整过程,每插入一个数据,或者删除一个数据都要对整个堆进行调整,使其符合小顶堆的要求。