置换选择排序分析
手写小根堆
解法一:手写小根堆 模拟文章来源:https://www.toymoban.com/news/detail-774583.html
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
using namespace std;
/*
对于所有输入
先将前m个数 存入小根堆
从第m+1个数开始遍历
设t为小根堆堆顶 cnt为小根堆内元素数量
idx代表当前归并段编号 a[i]为当前遍历到的数
如果a[i]>=t 意味着a[i]可以并入当前归并段
t弹出存入当前归并段 a[i]存入小根堆
如果a[i]<t 意味着a[i]属于下一个归并段
t弹出存入当前归并段 a[i]存储在小根堆之外
注意 此时小根堆内的元素数量cnt-=1
我们每一轮都要弹出小根堆堆顶
但是 由于a[i]<t的情况存在 cnt是会归零的
意味着没有元素可以弹出了
此时 存储在小根堆之外的元素 就可以组成一个新的小根堆
他们属于同一个归并段 idx++
因此 在每一次弹出小根堆堆顶前 都要检查小根堆是否为空
若为空 将存在小根堆之外的数组成一个小根堆,idx++
这里提供一个偷鸡的实现方式
heap[1]=heap[cnt--],down(1)//实现堆顶元素的弹出
heap[cnt+1]=a[i]//还是存在heap数组 但是不在小根堆内
然后对于cnt=0的时候 要将其组成一个小根堆
for(int i=cnt/2;i>=1;i--) down(i)
进行初始化就好了
*/
int n,m;
int a[100010];
int mp[100010];
int heap[100010],cnt;
void down(int u){
int t=u;
if (u*2<=cnt&&heap[t]>heap[u*2]) t=u*2;
if (u*2+1<=cnt&&heap[t]>heap[u*2+1]) t=u*2+1;
if (t!=u) {
swap(heap[u],heap[t]);
down(t);
}
}
void up(int u){
while (u>1&&heap[u]<heap[u/2]) {
swap(heap[u],heap[u/2]);
u/=2;
}
}
int main (){
scanf("%d%d", &n,&m);
for (int i=1;i<=n;i++) {
scanf ("%d",&a[i]);
if (i<=m) {//插入堆
heap[++cnt]=a[i];
up(cnt);
}
}
int idx=0;//
int last;
vector<vector<int>> ans(100010);//答案存储在这个二维数组 idx为下标
for (int i=m+1;i<=n;i++){
if (!cnt) {//因为每一轮都要弹出 所以要进行判断
cnt=m,idx++;
for (int i=cnt/2;i>=1;i--) down(i);
}
int t=heap[1];//堆顶
if (a[i]>=t) {//a[i]是本归并段
heap[1]=a[i],down(1);//a[i]插入小根堆
//实际上是将a[i]放在弹出后空缺的堆顶 因此cnt无变化
}
else {//a[i]是下一个归并段的
heap[1]=heap[cnt--],down(1);//弹出堆顶
heap[cnt+1]=a[i];//a[i]存在小根堆之外
}
ans[idx].push_back(t);//t都是存入当前归并段的
}
//此时循环结束 还有m个数在heap数组中
//其中1~cnt同属一个归并段 但是是否与idx为一个归并段不清楚
//cnt+1~m属于1~cnt后面的归并段
if (!cnt) {//涉及heap[1]是否属于当前归并段 看cnt
cnt=m,idx++;
for (int i=cnt/2;i>=1;i--) down(i);
}
for (int i=1;i<=cnt;i++) ans[idx].push_back(heap[i]);
if (cnt<m) idx++;//cnt+1~m属于1~cnt后面的归并段
for (int i=cnt+1;i<=m;i++) ans[idx].push_back(heap[i]);
for (int i=0;i<=idx;i++){//输出答案 注意回车空格
if (i)puts("");
sort(ans[i].begin(),ans[i].end());
for (int j=0;j<ans[i].size();j++){
if (j) printf (" ");
printf ("%d",ans[i][j]);
}
}
return 0;
}
解法二:双数组存储模拟
参考自文章来源地址https://www.toymoban.com/news/detail-774583.html
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <queue>
using namespace std;
// 对于一个序列中的数 其不是当前归并段就是下一个归并段的
// 以弹出数的数量为标记
// 每一轮以是否遍历完了原始序列进行判断
// 未遍历完 将a[i]存入对应归并段
// 每一轮都将堆顶存入now归并段
// 操作完后检查堆是否为空
// 若为空 说明当前归并段气数已尽
// 将next归并段全部存入小根堆
// next now数组clear
// 新的循环开始
int n,m;
int a[100010];
priority_queue <int,vector<int>,greater<int>> q;
int main (){
scanf("%d%d", &n,&m);
for (int i=1;i<=n;i++) scanf ("%d",&a[i]);
for (int i=1;i<=m;i++) q.push(a[i]);
vector <int> now,next;//本归并段 下一个归并段
int cnt=0;
int idx=m+1;
while (cnt<n){
int t=q.top();
q.pop();
cnt++;
now.push_back(t);
if (idx<=n){//数组未遍历完
if (t<=a[idx]) {//属于一个归并段 存入堆
q.push(a[idx++]);
}
else {//属于下一个归并段
next.push_back(a[idx++]);
}
}
if (q.empty()){//恒纪元已经结束嘞!
sort(now.begin(),now.end());
for (int i=0;i<now.size();i++){//输出本轮
if (i) printf (" ");
printf ("%d",now[i]);
}
printf ("\n");
now.clear();
for (auto x:next) q.push(x);
next.clear();
}
}
return 0;
}
到了这里,关于PAT甲级真题1171 Replacement Selection(置换选择) 双解法 带注释的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!