题目一
解题思路
堆排序(降序)的核心思想:
因为建小堆可以选出最小的数即根节点,我们将每次建好的小堆的最后一个叶子节点和根节点进行交换,交换后不把最后一个数看作堆里的数据,此时根的左右子树依旧是大堆,然后我们再用向下调整算法选出次小的如此循环直到堆里剩一个数结束
i为什么从n/2开始down
变量与边界
down操作中,u负责存储原数,比较时要以用u为准,t在比较过程中可能被修改。
堆的物理结构为数组,实现时为了方便计算,从下标1开始存储。
代码模板
#include<iostream>
using namespace std;
const int N=1e5+10;
int n,m;
int q[N],qsize;
void down(int u)
{
int t=u;
if(u*2<=qsize&&q[t]>q[u*2])t=u*2;
if(u*2+1<=qsize&&q[t]>q[u*2+1])t=u*2+1;
if(t!=u)
{
swap(q[u],q[t]);
down(t);
}
}
int main()
{
cin>>n>>m;
qsize=n;
for(int i= 1;i<=n;i++)
cin>>q[i];
for(int i=n/2;i;i--)
down(i);
while(m--)
{
cout<<q[1]<<" ";
q[1]=q[qsize--];
down(1);
}
return 0;
}
题目二
解题思路
依旧是使用数组模拟堆,核心思路是用另外两个数组表示堆到顺序,顺序到堆的映射关系
代码注意点:
hsize表示堆下标,index表示顺序下标,修改数值时要注意使用的下标;
修改第K个数时,先通过顺序数组求出K数对应的堆下标,再在堆中进行修改
代码模板
#include<iostream>
using namespace std;
const int N=1e5+10;
int ph[N],hp[N],h[N],n;//ph(point->heap)可以获得第几个插入的元素现在在堆里的哪个位置
//hp(heap->point)可以获得在堆的第n个元素存的是第几个插入的元素
int index,hsize;
void h_swap(int i,int j)
{
swap(ph[hp[i]],ph[hp[j]]);
swap(hp[i],hp[j]);
swap(h[i],h[j]);
}
void down(int u)
{
int t=u;
if(u*2<=hsize && h[u*2]<h[t])t=u*2;
if(u*2+1<=hsize && h[u*2+1]<h[t])t=u*2+1;
if(u!=t)
{
h_swap(u,t);
down(t);
}
}
void up(int u)
{
while(u/2&&h[u/2]>h[u]){
h_swap(u,u/2);
u>>=1;
}
}
int main()
{
scanf("%d",&n);
while(n--)
{
string t;
int k,x;
cin>>t;
if(t=="I")//插入一个数 x
{
cin>>x;
index++; hsize++;
ph[index]=hsize,hp[hsize]=index;
h[hsize]=x;
up(hsize);
}
else if(t=="PM")//输出当前集合中的最小值;
{
cout<<h[1]<<endl;
}
else if(t=="DM")//删除当前集合中的最小值(数据保证此时的最小值唯一)
{
h_swap(1,hsize);
hsize--;
down(1);
}
else if(t=="D")//删除第 k个插入的数;
{
cin>>k;
k=ph[k];
h_swap(k,hsize);
hsize--;
up(k),down(k);
}
else if(t=="C")//修改第k个插入的数,将其变为 x;
{
cin>>k>>x;
k=ph[k];
h[k]=x;
up(k),down(k);
}
}
return 0;
}