什么是最大堆
对于任意一个节点的值,它的子节点的值都不大于它的值。也就是说,最大堆的根节点是整个堆中最大的元素。在最大堆中,每个节点的值都大于等于其子节点的值。
应用场景
优先队列、堆排序等。
最大二叉堆
插入
逻辑
- 在堆的最后一个位置添加新元素,并将其上浮到正确位置。
- 将新元素与其父节点进行比较,如果新元素大于其父节点,则交换它们的位置,并继续向上比较,直到新元素不再大于其父节点或者到达根节点位置。
graph TD
将新元素添加到堆的最后一个位置 --> 新元素的下标
新元素的下标 --> 如果新元素大于它的父节点
如果新元素大于它的父节点 --> 交换新元素和它的父节点
交换新元素和它的父节点 --> 将新元素的下标更新为其父节点的下标
示意图
visualgo.net/zh/heap 这里可以进行堆的创建、插入、删除等操作
代码块
/**
* 插入
* @param nums 原数组
* @param val 新插入的节点值
*/
public static void insert(int[] nums, int val) {
//数组结尾的索引
int n = nums.length - 1;
nums[n] = val;
//当前索引为数组结尾
int curIndex = n;
//当前索引的父节点索引
int parentIndex = (n - 1) /2;
//当前索引>0且当前索引节点的值大于父节点索引的值
while (curIndex > 0 && nums[curIndex] > nums[parentIndex]) {
//交换当前索引节点和父节点
swap(nums, curIndex, parentIndex);
//更新当前索引为父节点索引
curIndex = parentIndex;
//更新当前索引的父节点
parentIndex = (curIndex - 1)/2;
}
}
对于索引为index的结点,其父结点的索引为(index - 1)/2。
删除
逻辑
- 删除根节点,并将堆的最后一个元素移动到根节点的位置。
- 将新的根节点与其子节点进行比较,如果新的根节点小于其子节点,则与其子节点中的最大值进行交换,并继续向下比较,直到新的根节点不再小于其子节点或者到达叶节点位置。
graph TD
堆大小 --> 堆为空 --> return
堆大小 --> 堆内元素大于等于1
堆内元素大于等于1 --> 保存堆最大值即堆顶元素 --> 将堆的最后一个元素移动到根节点的位置
将堆的最后一个元素移动到根节点的位置 --> 默认左节点的值比较大为larger
默认左节点的值比较大为larger --> 如果右节点大于larger --> 将右结点更新为larger
默认左节点的值比较大为larger --> 如果larger大于当前节点 --> 将左节点与当前节点交换 --> 更新下标
代码块
public static void delete(int[] nums) {
if (nums.length == 0) {
return;
}
int n = nums.length - 1;
// 用堆中最后一个元素来替换根节点
nums[0] = nums[n];
nums[n] = 0;
// 初始化当前索引及左右子节点索引
int curIndex = 0;
int leftIndex = 1;
int rightIndex = 2;
// 循环直到左子节点索引小于堆的大小
while (curIndex < n - 1) {
// 找到左右子节点中较大的一个
int largerIndex = leftIndex;
if (rightIndex < n - 1 && nums[rightIndex] > nums[leftIndex]) {
largerIndex = rightIndex;
}
// 如果当前元素小于较大子节点,则交换这两个元素,并更新索引
if (nums[curIndex] < nums[largerIndex]) {
swap(nums, curIndex, largerIndex);
curIndex = largerIndex;
leftIndex = 2 * curIndex + 1;
rightIndex = 2 * curIndex + 2;
} else {
// 否则跳出循环
break;
}
}
}
交换函数
private static void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
总结
最大堆的插入,主打一个上浮,先将新元素插入到数组的末尾,然后将当前元素与其父节点比较。上浮到合适的位置即可。 最大堆的删除,主打一个下沉,每次删除都是先删除堆顶的元素,然后将最后一个元素放到堆顶的位置,然后通过下沉到合适的位置即可。
全部代码
package algo;
import java.util.Arrays;
public class MaxHeap {
public static void main(String[] args) {
int[] nums = new int[7];
nums[0] = 13;
nums[1] = 10;
nums[2] = 12;
nums[3] = 8;
nums[4] = 6;
nums[5] = 11;
System.out.println(Arrays.toString(nums));
insert(nums, 14);
System.out.println(Arrays.toString(nums));
delete(nums);
System.out.println(Arrays.toString(nums));
}
/**
* 插入
* @param nums 原数组
* @param val 新插入的节点值
*/
public static void insert(int[] nums, int val) {
int n = nums.length - 1;
nums[n] = val;
//当前索引为数组结尾
int curIndex = n;
//当前索引的父节点索引
int parentIndex = (n - 1) /2;
//当前索引>0且当前索引节点的值大于父节点索引的值
while (curIndex > 0 && nums[curIndex] > nums[parentIndex]) {
//交换当前索引节点和父节点
swap(nums, curIndex, parentIndex);
//更新当前索引为父节点索引
curIndex = parentIndex;
//更新当前索引的父节点
parentIndex = (curIndex - 1)/2;
}
}
public static void delete(int[] nums) {
if (nums.length == 0) {
return;
}
int n = nums.length - 1;
// 用堆中最后一个元素来替换根节点
nums[0] = nums[n];
nums[n] = 0;
// 初始化当前索引及左右子节点索引
int curIndex = 0;
int leftIndex = 1;
int rightIndex = 2;
// 循环直到左子节点索引小于堆的大小
while (curIndex < n - 1) {
// 找到左右子节点中较大的一个
int largerIndex = leftIndex;
if (rightIndex < n - 1 && nums[rightIndex] > nums[leftIndex]) {
largerIndex = rightIndex;
}
// 如果当前元素小于较大子节点,则交换这两个元素,并更新索引
if (nums[curIndex] < nums[largerIndex]) {
swap(nums, curIndex, largerIndex);
curIndex = largerIndex;
leftIndex = 2 * curIndex + 1;
rightIndex = 2 * curIndex + 2;
} else {
// 否则跳出循环
break;
}
}
}
private static void swap(int[] nums, int index1, int index2) {
int temp = nums[index1];
nums[index1] = nums[index2];
nums[index2] = temp;
}
}