堆排序算法之前K个最大/最小值

堆排序算法:
需要根据输入的元素,构建一个大根/小根堆,即完全二叉树(不存在没有左孩子或右孩子的非叶子结点,以数组保存所有结点为例,父子结点的关系为:left_child_idx = 2 * parent_idx + 1, right_child_idx = 2 * parent_idx + 2),其中每个非叶子结点的值均大于/小于其左右孩子,即根结点值为所有结点值中的最大/最小值。

构建大根堆的过程描述如下:

从最后一个非叶子结点开始遍历,直到根结点结束,如果当前非叶子结点为左右孩子中的最大值,则不做任何操作,跳过当前结点,开始调整前一个非叶子结点。
如果当前非叶子结点的左孩子的值比其父结点和兄弟结点大,则交换父结点和左孩子的值,
由于交换后的左孩子的值可能并不比其左右孩子大(此左孩子结点为非叶子结点),因此需要从当前结点从上往下调整左子树,直到叶子结点。
同理如果当前非叶子结点的右孩子的值比其父结点和兄弟结点大,则与其父结点交换,并调整右子树。

    int main() {
        GetLeastNumbers_Solution(vector<int>([5,7,3,2,10,2,9]), 4);
    	return 0;
   	}
    // 堆排序,建立大根堆,找出前K个最大值
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        if (input.size() < k) return vector<int>();
        for (int i = 0; i < k; i++) {
            buildMinHeap(input, input.size() - i);
        }
        return vector<int>(input.end() - k, input.end());
    }
    // 构建小根堆
    void buildMinHeap(vector<int> &input, int len) {
        // 最后一个非叶子结点的下标为len / 2 - 1
        for (int i = len / 2 - 1; i >= 0; i--) {
            // 尝试比较当前结点和其左右孩子的大小,并交换
            // 左孩子下标 = 2 * i + 1
            // 右孩子下标 = 2 * i + 2
            adjustMinSubTree(input, i, len);
        }
        swap(input, 0, len - 1);
    }
    // 构建大概堆
    void buildMaxHeap(vector<int> &input, int len) {
        // 最后一个非叶子结点的下标为len / 2 - 1
        for (int i = len / 2 - 1; i >= 0; i--) {
            // 尝试比较当前结点和其左右孩子的大小,并交换
            // 左孩子下标 = 2 * i + 1
            // 右孩子下标 = 2 * i + 2
            adjustMaxSubTree(input, i, len);
        }
        swap(input, 0, len - 1);
    }
    void adjustMinSubTree(vector<int> &input, int i, int len) {
        int smaller = i;
        if (2 * i + 1 < len && input[2*i+1] < input[smaller]) {
            smaller = 2*i + 1;
        }
        if (2 * i + 2 < len && input[2*i+2] < input[smaller]) {
            smaller = 2*i+2;
        }
        if (smaller != i) {
            swap(input, i, smaller);
            adjustMinSubTree(input, smaller, len);
        }
    }
    void adjustMaxSubTree(vector<int> &input, int i, int len) {
        int larger = i;
        if (2 * i + 1 < len && input[larger] < input[2*i+1]) {
            larger = 2*i + 1;
        }
        if (2 * i + 2 < len && input[larger] < input[2*i+2]) {
            larger = 2*i+2;
        }
        if (larger != i) {
            swap(input, i, larger);
            adjustMinSubTree(input, larger, len);
        }
    }
    
    void swap(vector<int> &input, int i, int j) {
        int tmp = input[i];
        input[i] = input[j];
        input[j] = tmp;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值