堆排序(算法题)

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;

const int N = 100010;  // 堆数组的最大容量
int h[N], s;           // h[]存储堆元素,s表示当前堆的大小

// 下沉操作:调整以i为根的子树,维护小顶堆性质
void down(int i) {
    int t = i;  // t记录当前节点及其子节点中的最小值位置
    
    // 检查左子节点(2*i)是否存在,若更小则更新t
    if (2 * i <= s && h[i] > h[2 * i]) 
        t = 2 * i;
    
    // 检查右子节点(2*i+1)是否存在,且是否比当前t位置的更小
    if (2 * i + 1 <= s && h[t] > h[2 * i + 1]) 
        t = 2 * i + 1;
    
    // 若t发生变化,说明需要交换并继续调整
    if (t != i) {
        swap(h[t], h[i]);  // 交换当前节点与更小的子节点
        down(t);           // 递归调整交换后的子树
    }
}

int main() {
    int n, m;
    cin >> n >> m;        // 输入元素总数n和需要输出的前m小元素个数
    for (int i = 1; i <= n; i++) cin >> h[i];  // 输入数组(堆的初始状态)
    s = n;  // 初始化堆大小为n
    
    // 构建初始堆:从最后一个非叶子节点开始,自底向上调整
    // 模拟:例如n=5时,i从2开始处理,然后i=1。每个节点下沉到合适位置
    // 这样可以更短的时间复杂度构建
    for (int i = n / 2; i; i--) 
        down(i);
    
    // 输出前m小元素:每次取堆顶(最小值),然后维护堆
    while (m--) {
        cout << h[1] << ' ';  // 输出当前堆顶(最小元素)
        
        // 删除堆顶操作:用最后一个元素覆盖堆顶,堆大小减1
        h[1] = h[s]; 
        s--;
        
        // 调整新堆顶元素,使其下沉到合适位置
        // 模拟:例如将原本最后的元素放到堆顶后,可能破坏堆结构,需要逐层比较下沉
        down(1); 
    }
    return 0;
}

/*
模拟示例(n=5, m=3,初始数组[3,1,2,4,5]):
1. 初始建堆:
   - i=2(元素1):无子节点,无需调整
   - i=1(元素3):
     - 比较左子节点1(更小),交换3和1 → 数组[1,3,2,4,5]
     - 递归调整位置2(原3):
       - 比较子节点4和5,无需调整
2. 第一轮输出:
   - 输出1 → 堆顶替换为5,s=4 → 数组[5,3,2,4]
   - 调整堆顶5:
     - 左子3 > 右子2,交换5和2 → [2,3,5,4]
     - 递归调整位置3(5),无子节点,停止
3. 第二轮输出:
   - 输出2 → 堆顶替换为4,s=3 → 数组[4,3,5]
   - 调整堆顶4:
     - 左子3更小,交换4和3 → [3,4,5]
4. 第三轮输出:
   - 输出3 → 堆顶替换为5,s=2 → 数组[5,4]
   - 调整堆顶5:
     - 左子4更小,交换 → [4,5]
最终输出序列:1 2 3
*/

本篇参考了acwing算法基础课。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值