面试考察频率:⭐⭐⭐⭐
什么是大/小顶堆?
大/小根堆的实现可以看似为一颗完全二叉树,但和完全二叉树还是有区别的(具体的完全二叉树在之后会讲。
堆顶元素为整个堆的最大/小元素。
使用情景?
局部元素排序、实现优先队列、SPFA优化
如何来实现?
构建思路如下
⚪操作只有Push和Pop。
⚪文中通过数组来实现,本文主要讲解大顶堆(小顶堆同理),只要任意保证子节点小于父节点就可以。存储可以从下标从0开始或下标从1开始,个人更偏向于使用从1开始存储的方式操作更方便。
⚪从0开始存储时,获取子节点的公式为:左孩子为parent2+1,右孩子为parent2+2。 获取父节点的公式为:(child-1)/2
⚪从1开始存储时,获取子节点的公式为:左孩子为parent2,右孩子为parent2+1。 获取父节点的公式为:child/2。
基础结构表示:
int[] heap = new int[1000];
int heapSize = 0;
Push元素:
public void Push(int x)
{
//思路:每次先将新元素放到最后,在进行调整。从下向上,比较父元素
heap[++heapSize] = x;
int now = heapSize, nxt = 0;
while (now > 1)
{
nxt = now / 2;
if (heap[now] <= heap[nxt]) break;
Swap(ref heap[now], ref heap[nxt]);
now = nxt;
}
}
每次讲新元素放到堆的最后,在从下向上进行调整。
如果父节点比子节点小就交换当前子节点和父节点,为了始终保持父节点大于子节点。
如果出现父节点大于子节点的情况退出循环。
Pop元素:
public int Pop()
{
//思路:把顶的元素先存到零时变量中,在将堆末尾的元素覆盖到顶元素完成删除。之后再由顶
// 向下进行调整。上一个元素比较子两个元素,其中要比较两个子元素的大小,选择最大/ 小的与上边的元素进行交换
int res = heap[1];
int now = 1, nxt = 0;
heap[1] = heap[heapSize--];
while (now * 2 <= heapSize)
{
nxt = now * 2;
if (heap[nxt + 1] > heap[nxt] && nxt + 1 <= heapSize) nxt++;
if (heap[nxt] <= heap[now])
{
return res;
}
Swap(ref heap[nxt], ref heap[now]);
now = nxt;
}
return res;
}
每次将堆顶元素保存到临时变量中(因为堆顶元素要Pop出去,最后还要返回堆顶元素所以需要保存下来)。
之后把堆尾元素覆盖到堆顶元素去,再从上到下调整整个堆。
其中要注意,由于子节点有两个,一定要选最大的进行于父节点交换。所以注意这个情况的判定。
完整代码如下,更多数据C#数据结构源码欢迎浏览我的仓库:https://github.com/w199753/DataStructural-CSharp 最后附上完整代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataStructural
{
/// <summary>
/// 大根堆。小根堆实现方法相同
/// </summary>
public class MaxHeap
{
int[] heap = new int[1000];
int heapSize = 0;
/// <summary>
/// 添加元素
/// </summary>
/// <param name="x"></param>
public void Push(int x)
{
//思路:每次先将新元素放到最后,在进行调整。从下向上,比较父元素
heap[++heapSize] = x;
int now = heapSize, nxt = 0;
while (now > 1)
{
nxt = now / 2;
if (heap[now] <= heap[nxt]) break;
Swap(ref heap[now], ref heap[nxt]);
now = nxt;
}
}
/// <summary>
/// 删除元素并返回最大值
/// </summary>
/// <returns></returns>
public int Pop()
{
//思路:把顶的元素先存到零时变量中,在将堆末尾的元素覆盖到顶元素完成删除。之后再由顶
// 向下进行调整。上一个元素比较子两个元素,其中要比较两个子元素的大小,选择最大/ 小的与上边的元素进行交换
int res = heap[1];
int now = 1, nxt = 0;
heap[1] = heap[heapSize--];
while (now * 2 <= heapSize)
{
nxt = now * 2;
if (heap[nxt + 1] > heap[nxt] && nxt + 1 <= heapSize) nxt++;
if (heap[nxt] <= heap[now])
{
return res;
}
Swap(ref heap[nxt], ref heap[now]);
now = nxt;
}
return res;
}
public void Clear()
{
heap = new int[1000];
heapSize = 0;
}
private void Swap(ref int a, ref int b)
{
a ^= b;
b ^= a;
a ^= b;
}
}
}