内容概述
1,堆结构就是用数组实现的完全二叉树结构
2,完全二叉树中如果每棵子树的最大值都在顶部就是大根堆
3,完全二叉树中如果每棵子树的最小值都在顶部就是小根堆
4,堆结构的heaplnsert与heapify操作
5,堆结构的增大和减少
6,优先级队列结构,就是堆结构
1. 堆的定义
首先需要明确的是,堆是完全二叉树,完全二叉树不一定是堆(因为堆一定是排好序的【大根或者小跟】,完全二叉树就不一定了,不一定是堆)
这里有一篇文章,对堆描述的非常好【点击此处】
我们需要知道的是,堆是有大根堆和小根堆的,只有这两种特殊的完全二叉树才能称为堆
大根堆是指,每个根节点一定比其子节点大;小根堆是指,每个根节点一定比其子节点小
比方:
这个就是大根堆
根据这个特性,我们可以很快的拿到最大值或者最小值(均在根节点处)
但是,大根堆或者小根堆的数组,不一定是已经有序的,想变成有序的数组,还需要继续堆排序
2. 堆的数学属性
i是数组的某元素下标,现在需要知道该元素在堆中的父叶或者左子叶、右子叶的位置
父叶:parent(i) = (i - 1)/2
左子叶:left(i) = 2i + 1
右子叶:right(i) = 2i + 2
这是用的最多的堆的数学属性
3. 大根堆的插入
如果需要在一个大根堆里插入一个数,让其继续变成大根堆,应该怎么实现呢?
这就是HeapInsert过程
HeapInsert过程
当往一个大根堆里放入数的时候,先通过公式父叶:parent(i) = (i - 1)/2
找到该元素对应的父节点,然后将该元素与父叶比较,如果该元素大,则与父元素交换(后面如果又比该父叶的父叶大,则继续交换);如果父元素大,则该元素不掉换位置
程序:
public static void HeapInsert(int[] arr, int index)
{
//判断插入的元素与其父叶的大小,大则交换
while (arr[index] > arr[(index - 1) / 2])
{
Swap(arr, index, (index - 1) / 2);//交换元素的自定义方法
index = (index - 1) / 2;//下标换为其父叶坐标,继续判定
}
}
4. 大根堆的取元素
因为堆的根节点要么最大,要么最小,经常需要取出其元素的值,然后重新排序成大根堆的结构,这个过程叫做Heapify
Heapify
过程:
在某个堆中,取出某根节点(父叶),方法是将该元素与堆数组中最后一个元素互换后,比较交换后元素与根节点的左、右节点的最大值比较,叶大,则与叶交换;叶小,则不变
程序:
//取大根堆的某一元素后,把剩下的元素依然组成大根堆
//方法中的参数含义:arr-数组;index-已经交换后的元素下标;size-数组的长度
public static void Heapity(int[] arr, int index, int size)
{
//左子叶的下标
int left = index * 2 + 1;
//如果该父叶的左子叶存在,那么继续,不存在就跳出
while (left < size)
{
//右子叶存在,且找出左、右子叶的最大值的下标
int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
//将该下标对应的元素与父子叶对应的元素比较大小,当该下标对应的元素大于父子叶对应的元素时,交换元素
argest = arr[largest] > arr[index] ? largest : index;
//当该下标对应的元素小于或者等于父子叶对应的元素时,跳出循环
if (largest == index)
{
break;
}
//交换父子叶元素与最大值子叶下标元素的值
Swap(arr, largest, index);
//继续判断,此时的index下标变成刚刚的最大的子叶元素下标
index = largest;
//重新给左子叶赋值
left = index * 2 +