##[$AcWing$ $838$. 堆排序](https://www.acwing.com/problem/content/840/) ### 一、题目描述 输入一个长度为 $n$ 的整数数列,从小到大输出前 $m$ 小的数。 **输入格式** 第一行包含整数 $n$ 和 $m$。 第二行包含 $n$ 个整数,表示整数数列。 **输出格式** 共一行,包含 $m$ 个整数,表示整数数列中前 $m$ 小的数。 **数据范围** $1≤m≤n≤10^5$ , $1≤数列中元素≤10^9$ **输入样例:** ```cpp {.line-numbers} 5 3 4 5 1 3 2 ``` **输出样例:** ```cpp {.line-numbers} 1 2 3 ``` ### 二、堆的数据结构 **堆是一个完全二叉树**:除了最后一层结点以外,上面的每一层都是满的。最后一层的结点是从左到右排布的。 ![](https://img2020.cnblogs.com/blog/8562/202110/8562-20211013155723355-1612468409.png) **小根堆**:每一个点都是小于左右儿子的,所以根节点就是树中最小值.或者叫**小顶堆**。(**递归定义**) **存储方式**:全新的存储方式,用**一维数组**来存。因为是完全二叉树,所有数据的下标是有规则可以找到的。 位置$x$, 左儿子$2x$ , 右儿子$2x+1$ 下标是从$1$开始的,从$0$开始不方便,因为$2x$还是自己没法玩。 ### 三、堆操作的基本方法 * $down(x)$ 往下调整 比如现在堆已经维护好了,我们要把头结点的值 ```c++ 1 3 4 3 5 4 5 ``` 假设把头结点值换一下,换成6, ```c++ 6 3 4 3 5 4 5 ``` 现在就不是一个小顶堆了,因为$6$不比$3,4$小啊,所以需要对$6$进行调整,向下移动。 在$3,4,6$中找到一个最小值,然后交换$3,6$ ($down$操作时,如果当前结点大于左右儿子,与左右儿子中小的进行交换) ```c++ 3 6 4 3 5 4 5 ``` 然后继续在$6,3,5$中找最小值,继续交换$3,6$ ```c++ 3 3 4 6 5 4 5 ``` $OK$,移动完毕! * $up(x)$ 往上调整 比如现在堆已经维护好了,我们把$5$修改为$2$ ```c++ 3 3 4 3 5 4 2 ``` 就需要找出$4,2$ ,交换$4,2$ (与父结点对比,如果比父结点小,则交换自己与父结点) ```c++ 3 3 2 3 5 4 4 ``` 继续查询$3,2$,发现在$2<3$,继续交换 ```c++ 2 3 3 3 5 4 4 ``` 不再交换,$OK$,移动完毕。 ### 四、手写一个堆(小根堆) #### 1、插入一个数 ```cpp {.line-numbers} heap[++sz]=x; //在一维数组最后一个位置填充x up(sz); //将最后一个元素不断上移 ``` #### 2、求最小值 ```cpp {.line-numbers} heap[1] ``` #### 3、删除最小值 ```cpp {.line-numbers} heap[1]=heap[sz--]; //就是把尾部最后一个元素替换掉头元素,然后sz-- down(1); //然后再down(1)就行了 ``` #### 4、删除任意一个元素 ```cpp {.line-numbers} heap[k]=heap[sz--]; down(k); up(k); //其实只能执行一个,因为大了向下走。小了向上走嘛 ``` #### 5、修改任意一个元素 ```cpp {.line-numbers} heap[k]=x; down(k); up(k); ``` 两个基本操作,这两个操作结合起来就能完成上面五个操作。 $down(x)$ ---> 向下调整 $up(x)$ ---> 向上调整 ### 五、如何高效创建堆 ![](https://img2022.cnblogs.com/blog/8562/202202/8562-20220212200249491-486294802.png) ### 六、完整代码 ```cpp {.line-numbers} #include using namespace std; const int N = 1e5 + 10; int n, m; int sz; int heap[N]; void down(int u) { int t = u; if (u * 2 <= sz && heap[u * 2] < heap[t])t = u * 2; if (u * 2 + 1 <= sz && heap[u * 2 + 1] < heap[t])t = u * 2 + 1; if (u != t) { swap(heap[u], heap[t]); down(t); } } void up(int u) { while (u / 2 && heap[u / 2] > heap[u]) { swap(heap[u / 2], heap[u]); u /= 2; } } int main() { cin >> n >> m; for (int i = 1; i <= n; i++) cin >> heap[i]; sz = n; for (int i = n / 2; i >= 1; i--) down(i); while (m--) { printf("%d ", heap[1]); heap[1] = heap[sz--]; down(1); } return 0; } ```