堆排序在TOP K问题中的应用

问题

从数组中找出最大或者最小的k个数。

思路

以最小的k个数为例。可以使用一个大小为k的数组,然后依次遍历原始数据,当有元素比数组里的元素小时,就用这个数据将其替换出来。思路是对的,但是从大小为k的数组里面搜索最大元素的复杂度是O(n)。接下来优化一下,我们知道堆排序获得最大值(最小值)的复杂度是O(1),调整堆的复杂度是O(log n)。在海量数据处理的时候这个优化的效果是很明显的。

代码

题目描述:
输入n个整数,找出其中最小的K个数。

class BigHeap{//大顶堆,用于从k个数中获取最大值。
private:
    vector<int> data;//堆是完全二叉树,用数组存放
    int len;//堆中元素的个数
public:
    BigHeap(int n):len(n){}
    void push(int elem){//把元素放入堆中,然后调整堆
        int size = data.size();
        if(len < size)
            data.at(len) = elem;
        else
            data.push_back(elem);
        len += 1;
        int fa_idx = (len-2)/2;//新元素父节点下标
        int new_idx = len-1;//新元素的下标
        while(fa_idx >= 0){//每次讲新元素放到尾部,然后向上调整,直到满足条件或者到堆顶。
            if(data.at(new_idx) > data.at(fa_idx)){//因为是大顶堆,所以比父亲大就与父亲交换
                int tmp = data.at(new_idx);
                data.at(new_idx) = data.at(fa_idx);
                data.at(fa_idx) = tmp;
                new_idx = fa_idx;
                fa_idx = (fa_idx-1)/2;
            }
            else{
                break;
            }
        }
        printHeap();
    }
    int getMax(){//堆顶元素就是最大值
        if(len > 0)
            return data.at(0);
        else
            return 100000;
    }
    void printHeap(){//调试使用
        for(int i = 0; i < len; i++)
            cout << data.at(i) << " ";
        cout << endl;
    }
    void deleteMax(){//删除堆顶元素,然后调整堆
        if(len < 1)
            return;
        data.at(0) = data.at(len-1);//删除堆顶元素的方法是与最后的元素交换,然后减小元素个数len
        len -= 1;
        int fa_idx = 0;
        int lch_idx = (0+1)*2 - 1;
        int rch_idx = (0+1)*2;
        while(lch_idx < len){//调整堆的方法是向下调整,直到满足条件或者到堆尾
            int max_idx;
            if(rch_idx >= len){
                max_idx = lch_idx;
            }
            else{
                if(data.at(lch_idx) > data.at(rch_idx)){
                    max_idx = lch_idx;
                }
                else{
                    max_idx = rch_idx;
                }
            }
            if(data.at(fa_idx) < data.at(max_idx)){//向下调整是与孩子中最大的交换
                int tmp = data.at(fa_idx);
                data.at(fa_idx) = data.at(max_idx);
                data.at(max_idx) = tmp;
                fa_idx = max_idx;
                lch_idx = (max_idx + 1) * 2 -1;
                rch_idx = (max_idx + 1) * 2;
            }
            else{
                break;
            }
        }
        printHeap();
    }
};
class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        int len = input.size();
        if(k > len){
            vector<int> ans;
            return ans;
        }

        BigHeap myBigHeap(0);
        int i;
        for(i = 0; i < k; i++){//现将前k个元素放入堆中
            myBigHeap.push(input.at(i));
        }
        while(i < len){//如果新元素比堆中最大元素小,则删除堆中最大元素,并将新元素入堆
            int max = myBigHeap.getMax();
            if(input.at(i) < max){
                myBigHeap.deleteMax();
                myBigHeap.push(input.at(i));
            }
            i++;
        }
        vector<int> ans(k, 0);//返回排序好的最小k个数
        for(i = k-1; i >= 0; i--){
            ans.at(i) = myBigHeap.getMax();
            myBigHeap.deleteMax();
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值