这篇文章我们来讲一下堆
目录
1.堆的概述
计算机科学中,堆是一种基于树的数据结构,通常用完全二叉树实现。堆的特性如下:
- 在大顶堆中,任意节点C与它的父节点P符合P.value>=C.value
- 在大顶堆中,任意节点C与它的父节点P符合P.value<=C.value
- 最顶层的节点(没有父亲)称之为root根节点
注意:完全二叉树指除了最后一层,上层全部填满。并且最后一层填充时是从左往右填充。
堆是一种树形结构,但是我们依然可以用数组来存储它,下面看一下数组存储的特征:
如果从索引О开始存储节点数据
- 节点 i 的父节点为floor((i- 1)/2),当i>0时
- 节点 i 的左子节点为 2i+1,右子节点为 2i+2,当然它们得<size
如果从索引1开始存储节点数据
- 节点 i 的父节点为 floor(i/2),当i >1时
- 节点主的左子节点为2i,右子节点为2i+1,同样得<size
2.堆的实现
下面看一下具体的代码:
代码如下:
import java.util.Arrays;
public class L17_MaxHeap {
int[] array;//存储的数组
int size;//有效元素个数
/**下面是针对两种不同场景的两种构造方法*/
public L17_MaxHeap(int capacity) {
this.array = new int[capacity];
}
public L17_MaxHeap(int[] array) {
this.array = array;
this.size = array.length;
heapify();
}
/**建堆操作*/
private void heapify(){
//找到最后一个非叶子节点 (size/2)-1 (索引0为起点的)
for (int i =size/2-1; i >=0 ; i--) {
down(i);
}
}
/**删除堆顶元素*/
public int poll(){
int top = array[0];
swap(0,size-1);//交换堆顶和数组中最后的一个元素
size--;
down(0);//让堆顶的元素(现在是最后的一个)进行下潜,到达此元素的正确位置
return top;
}
/**删除指定位置处的元素*/
public int poll(int index){//和上面的逻辑相似
int deleted = array[index];
swap(index,size-1);
size--;
down(index);
return deleted;
}
/**返回指定位置处的元素*/
public int peek(int index){
return array[0];
}
/**替换堆顶的元素*/
public void replace(int value){
array[0] = value;
down(0);
}
/**堆的尾部添加元素*/
public boolean offer(int offered){
if (size == array.length)
return false;
up(offered);
size++;
return true;
}
/**将offered元素上浮,直到offered小于父元素或者到达堆顶*/
private void up(int offered){
int child = size;//确定新加入元素的索引
while (child > 0){
int parent = (child-1)/2;//确定新加入元素父节点的索引
if (offered>array[parent]){//如果加入元素大于其父节点,交换两个元素
array[child] = array[parent];
}else {
break;//不大于,说明位置是对的,就退出循环
}
child = parent;//然后让加入元素的索引等于其父节点的索引
}
array[child] = offered;//对其赋值
}
/**将parent处的元素下潜,与两个孩子较大者交换,直到没孩子或者孩子没有它大*/
private void down(int parent){
int left = parent*2+1;//它的左孩子索引
int right = parent*2+2;//它的右孩子索引
int max = parent;//当前的父节点索引
if (left<size && array[left]>array[max]){//左孩子大于父节点
max = left;//交换左孩子与父节点的索引
}
if (right<size && array[right]>array[max]){
max = right;
}
if (max != parent){//找到最大的孩子
swap(max,parent);
down(max);
}
}
/**交换两个索引处的值*/
private void swap(int i,int j){
int a = array[i];
array[i] = array[j];
array[j] = a;
}
public static void main(String[] args) {
int[] array = {1,2,3,4,5,6,7};
L17_MaxHeap maxHeap = new L17_MaxHeap(array);
System.out.println(Arrays.toString(maxHeap.array));
}
}
关键在于构造和上浮和下潜这三个方法的书写。
3.总结
堆在数据结构上是和二叉树一样的,但是其数据的分布有自己的特点,根据这些特点,我们将其分为大顶堆和小顶堆。堆在实现上是用数组来实现的。我们需要牢牢的掌握堆元素下潜和上浮的代码书写。