leetcode topk海量数据

题目

在学习数据结构的过程,一定会遇到这样一道题目,海量数据中查找前k个最大/最小的数,LeetCode题目面试题40. 最小的k个数:输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

解题思路

入门级

  1. 整体排序:直接使用C++ sort函数,sort是基于快排的优化
class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        vector<int> ans;
        sort(arr.begin(), arr.end());
        for (int i = 0; i < k; ++i)
            ans.push_back(arr[i]);
        return ans;
    }
};
  1. 部分排序:直接进行k次遍历,找到前k个最小的数字,类似于简单选择排序(打扑克牌)
class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        int len = arr.size();
        int min, min_index;
        vector<int> ans;
        for (int i = 0; i < k; ++i)
        {
            min = arr[i];
            min_index = i;
            for (int j = i+1; j < len; ++j)
            {
                if (min > arr[j])
                {
                    min = arr[j];
                    min_index = j;   
                }
            }
            ans.push_back(arr[min_index]);
            arr[min_index] = arr[i];
        }
        return ans;
    }
};

进阶

  1. 快排:借助快排的特点,每一趟快排都会找到一个确切的位置,小的数在左边,大的数在右边,所以只要快排找到第k个位置的数字即可,不用对整个数组进行多余的排序操作。
class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        vector<int> ans;
        if (k==0)
            return ans;
        quick_sort(arr, 0, arr.size()-1, k);
        for (int i = 0; i < k; ++i)
            ans.push_back(arr[i]);
        return ans;
    }
    void quick_sort(vector<int> &arr, int low, int high, int k)
    {
        if (low < high)
        {
            int pivot_positon = partition(arr, low, high);
            if (pivot_positon > k-1 )
                quick_sort(arr, low, pivot_positon-1, k);
            else if (pivot_positon < k -1)
                quick_sort(arr, pivot_positon+1, high, k);
        }
    }
    int partition(vector<int> &arr, int low, int high)
    {
        int pivot = arr[low];
        while (low < high)
        {
            while (low<high && arr[high]>=pivot)//此处不要少了=,否则相同的数据会死循环
                --high;
            arr[low] = arr[high];
            while (low<high && arr[low]<pivot)
                ++low;
            arr[high] = arr[low];
        }
        arr[low] = pivot;
        return low;
    }
};
  1. 最小(大)堆:用堆作为数据结构来管理数据,堆是一棵完全二叉树,完全二叉树因为结构的特殊性,父节点的下标是子节点下标一半,所以通常用数组很好表示。堆排序分为构造堆和排序两个过程

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        if(k == 0) return vector<int>();
        vector<int> max_heap_vec(arr.begin(), arr.begin()+k);
        //对前k个数构造最大堆
        buildMaxHeap(max_heap_vec);
        //剩余的数插入堆里,堆顶部是最大值,出现比最大值还小的数时,替换掉堆顶,然后把堆顶元素向下调整,形成新的最大堆。
        for(int i = k; i<arr.size(); ++i){
            // 出现比堆顶元素小的值, 置换堆顶元素, 并调整堆
            if(arr[i] < max_heap_vec[0])
            {
                max_heap_vec[0] = arr[i];
                downAdjust(max_heap_vec, 0);
            }
        }
        return max_heap_vec;
    }
private:
    void buildMaxHeap(vector<int>& v){
        // 所有非叶子节点从后往前依次下沉
        for(int i = static_cast<int>(v.size()-1) / 2; i>=0; --i) //最后一个叶结点的父节点开始,父节点向下调整,直到根节点调整完成
        {
            downAdjust(v, i);
        }
    }

    void downAdjust(vector<int>& v, int index)
    {
        int parent = v[index];
        // 左孩子节点索引,若有子节点,左孩子必然存在,完全二叉树特点
        int child_index = 2*index;
        while(child_index < v.size()){
            // 判断是否存在右孩子, 并选出较大的节点
            if(child_index+1 < v.size() && v[child_index+1] > v[child_index]){
                ++child_index;
            }
            // 判断父节点和子节点的大小关系
            if(parent >= v[child_index])
                break;
            // 较大节点上浮
            v[index] = v[child_index];
            index = child_index;
            child_index = 2*index;
        }
        v[index] = parent;
    }
};

posted @ 2020-03-20 15:19  vito_wang  阅读(189)  评论(0编辑  收藏  举报