堆排序,模拟堆(手写)
堆排序
acwing838题https://www.acwing.com/problem/content/840/
堆
以小顶堆为例,堆是一个完全二叉树,所有结点都满足根节点大于左右儿子结点.
树的根节点是堆顶,是最小值.
用数组实现。下标从1开始,下标为x
的左儿子下标为2x
,右儿子下标为2x+1
手写堆
数组初始化成一个堆 n/2开始down
插入一个数 h[++ hsize] = x; up(hsize);
求集合最小值 h[1]
删除最小值 h[1] = h[hsize]; hsize --; down(1);
删除任意一个数 h[k] = h[hsize]; hsize --; down(k); up(k);
修改任意一个元素 h[k] = x; down(k); up(k);
down操作是如果根节点大就往下移
up操作是如果根节点小就往上移
down,up两操作都是递归
void down(int u)
{
int t = u;
if(2 * u <= hsize && h[t] > h[2*u]) t = 2 * u;
if(2 * u + 1 <= hsize && h[t] > h[2*u + 1]) t = 2 * u + 1;
if( u != t )
{
swap(h[t],h[u]);
down(t);
}
}
void up(int u)
{
int t = u;
if(2 * u <= hsize && h[t] < h[2*u]) t = 2 * u;
if(2 * u + 1 <= hsize && h[t] < h[2 * u + 1]) t = 2*u + 1;
if(u != t)
{
swap(h[t],h[u]);
up(t);
}
}
数组初始化成堆O(n)
for(int i = n/2; i > 0 ; i --)
{
down(i);
}
从n/2位置下标开始down,时间复杂度是O(n)
证明:https://www.acwing.com/video/263/视频26分处开始
堆排序代码(小根堆)
#include<iostream>
#include<algorithm>
using namespace std ;
const int N = 1e5 + 10;
int h[N],hsize;
int n,m;
void down(int u)
{
int t = u;
if(2 * u <= hsize && h[t] > h[2 * u]) t = 2 * u;
if(2 * u + 1 <= hsize && h[t] > h[2 * u + 1]) t = 2 * u + 1;
if(u != t)
{
swap(h[u],h[t]);
down(t);
}
}
int main()
{
scanf("%d %d",&n,&m);
hsize = n;
for(int i = 1 ; i <= n ; i ++) scanf("%d",&h[i]);
for(int i = n/2 ; i ; i --) down(i);
while(m --)
{
printf("%d ",h[1]);
h[1] = h[hsize --];
down(1);
}
return 0;
}
模拟堆
链接:AcWing839题.
因为要插入删除修改第k个插入到堆里面的数,就要多开数组来存放第k个插入的数在堆数组中的下标是多少。
ph[k] = cnt; // 第 k个插入的数的在堆数组下标为 cnt
hp[cnt] = k; // 堆数组下标为 cnt是第 k个插入的数
p[cnt] = x; // 堆数组下标为 cnt的数的值是 x
响应的需要补充一个heap_swap
函数,代替swap
// 因为补充了一个映射关系,所以在交换的时候也要交换ph[]、hp[]
void heap_swap(int a,int b) // a、b为堆数组中的下标
{
swap(ph[hp[a]],ph[hp[b]]);
swap(hp[a],hp[b]);
swap(h[a],h[b]);
}
其他代码就把swap改成heapswap即可
模拟堆代码(小根堆)
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std ;
const int N = 100010 ;
int h[N],ph[N],hp[N],cnt; // cnt 为堆数组下标
void heap_swap(int a,int b)
{
swap(ph[hp[a]],ph[hp[b]]);
swap(hp[a],hp[b]);
swap(h[a],h[b]);
}
void down(int u) // 小根堆的down操作,如果比两边的左右孩子小就交换
{
int t = u ;
if(2 * u <= cnt && h[2 * u] < h[t]) t = 2*u ; // 注意2*u <= cnt是可以取等的!
if(2 * u + 1 <= cnt && h[2 * u + 1] < h[t]) t = 2 * u + 1 ;
if(t != u)
{
heap_swap(t,u) ;
down(t) ;
}
}
void up(int u)
{
while(u / 2 && h[u] < h[u/2])
{
heap_swap(u,u/2);
u = u/2;
}
}
int main()
{
int n;
scanf("%d",&n);
int m = 0; // m 代表第m个插入的数
while(n --)
{
char op[10];
scanf("%s",op);
int k,x;
if(!strcmp(op,"I"))
{
scanf("%d",&x);
cnt ++;
m ++;
ph[m] = cnt, hp[cnt] = m;
h[cnt] = x;
up(cnt);
}
else if(!strcmp(op,"PM")) printf("%d\n",h[1]);
else if(!strcmp(op,"DM"))
{
heap_swap(1,cnt);
cnt --;
down(1);
}
else if(!strcmp(op,"D"))
{
scanf("%d",&k);
int idxk = ph[k];
heap_swap(idxk,cnt);
cnt --;
up(idxk);
down(idxk);
}
else
{
scanf("%d%d",&k,&x);
int idxk = ph[k];
h[idxk] = x;
up(idxk);
down(idxk);
}
}
return 0 ;
}
rds_blogs
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!