堆
介绍
1、堆是顺序储存的完全二叉树,即树是逻辑上的存储结构,数组是实际的存储结构
2、分类
(1)大顶堆:每个结点的值都大于或等于其左右孩子结点的值,arr[i]>=arr[2i+1]&&arr[i]>=arr[2i+2](i 对应节点下标,i从0开始编号),升序使用大顶堆
(2)小顶堆:每个结点的值都小于或等于其左右孩子结点的值,arr[i]<=arr[2i+1]&&arr[i]<=arr[2i+2](i 对应节点下标,i从0开始编号),降序使用小顶堆
(3)注意:不要求结点的左孩子的值和右孩子的值的大小关系
大顶堆
1、插入节点
(1)在堆的最后插入节点,即插入的节点为数组最后一个元素,保持堆为完全二叉树
(2)堆的非叶子节点的最大下标为 n / 2 - 1(n 为堆的节点总数),即查找最后一个非叶子节点
(3)以最后一个非叶子节点 A 视作根节点,构建一个局部的完全二叉树
(4)在父节点的两个或一个子节点中,选择值最大的子节点,与父节点值比较
(5)若子节点值更大,则交换将父节点与子节点交换,在父节点的新位置(即子节点原来的位置)重复(3)-(4),即向下调整堆,大节点上浮,小节点下沉,直到到达叶子节点或出现(6)的情况
(6)若父节点值更大,则不交换,此时(局部的)完全二叉树为大顶堆,转到(7)
(7)从 A 的位置向上遍历非叶子节点(向前遍历数组),重复(3)-(6),直到根节点
(8)堆恢复为大顶堆
2、删除最大值
(1)队列首尾元素交换,此时尾元素为最大,将尾元素出队
(2)此时需要调整队列结构,恢复成大顶堆,从根节点向下调整堆,大节点上浮,小节点下沉
(3)在父节点的两个或一个子节点中,选择值最大的子节点,与父节点值比较
(4)若子节点值更大,则交换将父节点与子节点交换,在父节点的新位置(即子节点原来的位置)重复(3),即向下调整堆,大节点上浮,小节点下沉,直到到达叶子节点或出现(5)的情况
(5)若父节点值更大,则不交换,此时完全二叉树为大顶堆
小顶堆
1、插入节点
(1)在堆的最后插入节点,即插入的节点为数组最后一个元素,保持堆为完全二叉树
(2)堆的非叶子节点的最大下标为 n / 2 - 1(n 为堆的节点总数),即查找最后一个非叶子节点
(3)以最后一个非叶子节点 A 视作根节点,构建一个局部的完全二叉树
(4)在父节点的两个或一个子节点中,选择值最小的子节点,与父节点值比较
(5)若子节点值更小,则交换将父节点与子节点交换,在父节点的新位置(即子节点原来的位置)重复(3)-(4),即向下调整堆,小节点上浮,大节点下沉,直到到达叶子节点或出现(6)的情况
(6)若父节点值更小,则不交换,此时(局部的)完全二叉树为小顶堆,转到(7)
(7)从 A 的位置向上遍历非叶子节点(向前遍历数组),重复(3)-(6),直到根节点
(8)堆恢复为小顶堆
2、删除最小值
(1)队列首尾元素交换,此时尾元素为最小,将尾元素出队
(2)此时需要调整队列结构,恢复成小顶堆,即从根节点向下调整堆,小节点上浮,大节点下沉
(3)在父节点的两个或一个子节点中,选择值最小的子节点,与父节点值比较
(4)若子节点值更小,则交换将父节点与子节点交换,在父节点的新位置(即子节点原来的位置)重复(3),即向下调整堆,小节点上浮,大节点下沉,直到到达叶子节点或出现(5)的情况
(5)若父节点值更小,则不交换,此时完全二叉树为小顶堆
代码实现
public class Heap {
public Node[] array;//数组储存结点
public int number;//有效节点个数
public Heap(int capacity) {
if (capacity > 0) {
this.array = new Node[capacity];
this.number = 0;//初始有效节点个数为0
}
}
//插入结点,构造大顶堆
public void maxHeap(Node node) {
this.expand();
array[number++] = node;
//i代表非叶子节最大的下标,下标从大到小遍历
for (int i = number / 2 - 1; i >= 0; i--) {
maxHeap(i);
}
}
//插入结点,构造小顶堆
public void minHeap(Node node) {
this.expand();
array[number++] = node;
//i代表非叶子节最大的下标,下标从大到小遍历
for (int i = number / 2 - 1; i >= 0; i--) {
minHeap(i);
}
}
//大节点上浮,小节点下沉
public void maxHeap(int i) {
int maxIndex = number - 1;//此时树中所有元素的最大下标
Node temp = array[i];//先取出当前元素的值,保存在临时变量
//k最初指向i的左子节点
for (int k = i * 2 + 1; k <= maxIndex; k = k * 2 + 1) {
//k+1(右子节点下标)<=maxIndex(树的最大下标),说明存在右子节点
//array[k]>array[k+1]说明右子结点的值大于左子结点的值
if (k + 1 <= maxIndex && array[k].no < array[k + 1].no) {
k++; //k指向i的右子结点
}
if (array[k].no > temp.no) {//如果子结点大于父结点
array[i] = array[k];//把较大的值赋给父节点
i = k; //i指向k,继续循环比较,向下调整结构
} else {
break;
}
}
//当循环结束后,已经将以i为父结点的树的最大值,放在了最顶(局部)
array[i] = temp;// 将temp值放到调整后的位置
}
//大节点下沉,小节点上浮
public void minHeap(int i) {
int maxIndex = number - 1;//此时树中所有元素的最大下标
Node temp = array[i];//先取出当前元素的值,保存在临时变量
//k最初指向i的左子节点
for (int k = i * 2 + 1; k <= maxIndex; k = k * 2 + 1) {
//k+1(右子节点下标)<=maxIndex(树的最大下标),说明存在右子节点
//array[k]>array[k+1]说明右子结点的值小于左子结点的值
if (k + 1 <= maxIndex && array[k].no > array[k + 1].no) {
k++; //k指向i的右子结点
}
if (array[k].no < temp.no) {//如果子结点小于父结点
array[i] = array[k];//把较小的值赋给父节点
i = k; //i指向k,继续循环比较,向下调整结构
} else {
break;
}
}
//当循环结束后,已经将以i为父结点的树的最小值,放在了最顶(局部)
array[i] = temp;// 将temp值放到调整后的位置
}
//数组扩容
public void expand() {
if (number + 1 > array.length) {
Node[] nodes = new Node[2 * number];
for (int i = 0; i < number; i++) {
nodes[i] = array[i];
}
array = nodes;
}
}
}
class Node {
public int no;
public Node(int no) {
this.no = no;
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战