const int N=1e5+10;
char str[N];
int son[N][26],cnt[N],idx;// 下标是0的点即使根节点也是空节点 // son[][]存储树中每个节点的子节点// cnt[]存储以每个节点结尾的单词数量voidinsert(char str[]){
int p=0;// 根节点 for(int i=0;str[i];i++){
int u= str[i]-'a';if(!son[p][u]) son[p][u]=++idx;
p = son[p][u];}
cnt[p]++;}
int query(char str[]){
int p=0;for(int i=0;str[i];i++){
int u=str[i]-'a';if(!son[p][u])return0;// 如果不存在子节点说明集合中没有该字符串
p=son[p][u];}return cnt[p];}
二、并查集
const int N=1e5+10;
int p[N];
int n,m;//维护一个size[i] 表示 i集合中有多少个数 只有i是根节点才有意义 for(int i=1;i<=n;i++){
p[i]= i;
size[i]=1;}
int find(int x)// 找祖宗节点+路径压缩 {if(p[x]!= x)
p[x]=find(p[x]);return p[x];}// 合并 voidunion_a(int a,int b){if(find(a)==find(b))continue;
size[find(b)]+=size[find(a)];
p[find(a)]=find(b); 让b的祖先 成为 a的祖先的祖先!! 挺不错的哈哈哈哈哈哈哈哈 }
三、模拟堆
#include<bits/stdc++.h>
using namespace std;const int N=1e5+10;// h[N]存储堆中的值, h[1]是堆顶,x的左儿子是2x, 右儿子是2x + 1// ph[k]存储第k个插入的点在堆中的位置// hp[k]存储堆中下标是k的点是第几个插入的// p是下标的意思
int h[N],ph[N],hp[N],size;//h数组存的是堆里面的元素 //hp 与 ph 的关系 //hp[i] -> ph[k]中的k //ph[i] -> hp[k]中的k voidheap_swap(int a,int b){swap(ph[hp[a]],ph[hp[b]]);swap(hp[a],hp[b]);swap(h[a],h[b]);}voiddown(int u){
int t=u;if(2*u <= size && h[2*u]< h[t]) t=2*u;if(2*u+1<=size && h[2*u+1]<h[t]) t=2*u+1;if(u!=t){heap_swap(h[t],h[u]);down(t);}}voidup(int u){while(u/2&& h[u/2]>h[u]){heap_swap(u/2,u);
u/=2;}}// O(n)建堆for(int i = n /2; i; i --)down(i);
3.1堆排序
#include<bits/stdc++.h>
using namespace std;const int N=1e5+10;
int h[N];
int n,m;
int size;voiddown(int x){
int t=x;if(2*x<=size && h[2*x]< h[t])
t=2*x;if(2*x+1<=size && h[2*x+1]<h[t])
t=2*x+1;if(x!=t){swap(h[t],h[x]);down(t);}}voidup(int x){while(x/2&& h[x/2]>h[x]){swap(h[x/2],h[x]);
x/=2;}}
int main(){scanf("%d %d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&h[i]);
size = n;for(int i=n/2;i;i--)down(i);while(m--){printf("%d ",h[1]);
h[1]= h[size--];down(1);}return0;}