二叉堆的详解
1、堆的定义
堆是一种数据结构,一种叫做完全二叉树的数据结构。
什么是二叉树?
二叉树是一种特殊的树。二叉树的客店是每个结点最多有两个儿子,左边的叫左儿子,右边的叫右儿子。
二叉树的分类
二叉树中还有两种特殊的二叉树,叫做满二叉树和完全二叉树。
如果二叉树中每个内部结点都有两个儿子,这样的二叉树叫做满二叉树。满二叉树的严格的定义是一棵深度为h且有 个结点的二叉树。
若一棵二叉树的高度为h,除第h层外,其他各层 的结点数都达到最大个数,第 层从右向左连续缺若干结点,则这个二叉树就是完全二叉树。
如何存储二叉树
首先将完全二叉树进行从上到下,从左到右编号。若一个父节结点编号为k,那么它左儿子的编号就是 ,右儿子的编号就是 。如果已知儿子(左儿子或右儿子)的编号为 ,那么它的父结点的编号为 (c++语言中除法只取整数部分)。
堆的性质
1.堆中某个结点的值均不大于(或不小于)其父结点的值。 2.堆是一棵完全二叉树。 假设一棵完全二叉树所有父结点都比子节点要小,这样的完全二叉树成为最小堆,最大堆同理。
堆的操作
堆支持以下操作:
1.插入:向堆中插入一个新结点。在数组的末尾加入新值,并从下到上调整堆的父子关系,如果该结点大于其父结点,则交换它和它的父结点,使得它所在的子树满足堆的性质,时间复杂度 。
void sift_up(int i)
{ //从下到上调整堆的父子关系
if (i == 1)
return;
while (i != 1)
{
if (h[i] < h[i / 2])
swap(h[i], h[i / 2]); //<为从小到大排序 >为从大到小排序
else
break;
i = i / 2;
}
//for(int i=1;i<=n;i++)cout<<h[i]<<" ";
//cout<<endl;
}
2.弹出:删除堆顶元素。将堆底的元素覆盖堆顶的元素,结点数-1,再从上到下调整堆的父子关系。时间复杂度 。
void erase(int x){//弹出
//cout<<h[x]<<" ";//排序时用
h[x]=h[n];
h[n]=0;
n--;//结点数-1
}
void erase(int x)
{ //弹出
//cout<<h[x]<<" ";//排序时用
h[x] = h[n];
h[n] = 0;
n--; //结点数-1
}
erase(1);
sift_down(1);
3.删除:将堆中的某个编号为x的元素删除。将堆底的元素覆盖该元素,结点数-1,并从该元素交换前的位置自顶向下调整堆的父子关系。时间复杂度 。
erase(x);
sift_down(x);
4、创建堆的两种方法
1、一边输入,一边执行插入操作。
for (int i = 1; i <= n; i++)
{
cin >> h[i];
sift_up(i);
//for(int j=1;j<=n;j++)cout<<h[j]<<" ";
//cout<<endl;
}
2、在全部输入完后,紧接着从最后一个非叶结点 遍历到根节点 ,将当前结点向下调整,时间复杂度为
for (int i = 1; i <= n; i++)
{
cin >> h[i];
//sift_up(i);
//for(int j=1;j<=n;j++)cout<<h[j]<<" ";
//cout<<endl;
}
for (int i = n / 2; i >= 1; i--)
sift_down(i); //注意这是sift_down,不是sift_up! ! !
堆的排序(重点来了! ! !)
1、将输入的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素; 2、重复执行 次弹出操作,在覆盖堆顶时先将堆顶输出(或存储); 3、时间复杂度为
大根堆
#include <cstring>
#include <bits/stdc++.h>
template <typename item>
class largest_heap
{
private:
item heap[10001];
int len;
public:
largest_heap();
void push(item const &);
void pop();
item top();
int size();
bool empty();
};
template <typename item>
largest_heap<item>::largest_heap()
{
len = 0;
memset(heap, 0, sizeof(heap));
}
template <typename item>
void largest_heap<item>::push(item const &n)
{
heap[++len] = n;
int son = len, father = son / 2;
while (heap[son] > heap[father] && father >= 1)
{
swap(heap[son], heap[father]);
son = father, father = son / 2;
}
}
template <typename item>
void largest_heap<item>::pop()
{
swap(heap[1], heap[len]);
heap[len--] = 0;
int father = 1, son = 2;
while (son <= len)
{
if (son < len && heap[son] < heap[son + 1])
son++;
if (heap[father] < heap[son])
{
swap(heap[father], heap[son]);
father = son, son = father * 2;
}
else
break;
}
}
template <typename item>
item largest_heap<item>::top()
{
return heap[1];
}
template <typename item>
int largest_heap<item>::size()
{
return len;
}
template <typename item>
bool largest_heap<item>::empty()
{
return len;
}
小根堆
#include <cstring>
#include <bits/stdc++.h>
template <typename item>
class smallest_heap
{
private:
item heap[10001];
int len;
public:
smallest_heap();
void push(item const &);
void pop();
item top();
int size();
bool empty();
};
template <typename item>
smallest_heap<item>::smallest_heap()
{
len = 0;
memset(heap, 0, sizeof(heap));
}
template <typename item>
void smallest_heap<item>::push(item const &n)
{
heap[++len] = n;
int son = len, father = son / 2;
while (heap[son] < heap[father] && father >= 1)
{
swap(heap[son], heap[father]);
son = father, father = son / 2;
}
}
template <typename item>
void smallest_heap<item>::pop()
{
swap(heap[1], heap[len]);
heap[len--] = 0;
int father = 1, son = 2;
while (son <= len)
{
if (son < len && heap[son] > heap[son + 1])
son++;
if (heap[father] > heap[son])
{
swap(heap[father], heap[son]);
father = son, son = father * 2;
}
else
break;
}
}
template <typename item>
item smallest_heap<item>::top()
{
return heap[1];
}
template <typename item>
int smallest_heap<item>::size()
{
return len;
}
template <typename item>
bool smallest_heap<item>::empty()
{
return len;
}
#include <bits/stdc++.h>
using namespace std;
int n, a, b;
int h[1000005];
void erase(int x)
{
cout << h[x] << " ";
h[x] = h[n--];
}
void sift_up(int i)
{
if (i == 1)
return;
while (i != 1)
{
if (h[i] < h[i / 2])
swap(h[i], h[i / 2]); //<为从小到大排序 >为从大到小排序
else
break;
i = i / 2;
}
//for(int i=1;i<=n;i++)cout<<h[i]<<" ";
//cout<<endl;
}
void sift_down(int i)
{
if (i > n)
return;
while (i * 2 <= n)
{
i = 2 * i;
if (i + 1 <= n && h[i + 1] < h[i])
i = i + 1; //<为从小到大排序 >为从大到小排序
if (h[i / 2] > h[i])
swap(h[i / 2], h[i]); //>为从小到大排序 <为从大到小排序 (这里不同!)《——
else
break;
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> h[i];
sift_up(i);
//for(int j=1;j<=n;j++)cout<<h[j]<<" ";
//cout<<endl;
}
//for(int i=n/2;i>=1;i--)sift_down(i);//注意这是sift_down,不是sift_up! ! !
while (n > 1)
{
erase(1);
sift_down(1);
}
//for(int i=1;i<=n;i++)cout<<h[i]<<" ";
return 0;
}
——————QAQ
本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/18122219
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)