返回顶部

题目:

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

输入: nums = [1], k = 1
输出: [1]

https://leetcode.cn/problems/top-k-frequent-elements/

方法一:

class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
         // 创建一个哈希表,用于记录每个元素出现的频率
        unordered_map<int, int> freqNum;
        for (int num : nums) {
            freqNum[num]++;
        }
        // 创建一个最小堆,用于存储频率及其对应的元素
        // 优先队列的比较函数为 greater<>,表示最小堆
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
        // 遍历哈希表,将频率和元素加入最小堆
        for (auto& entry : freqNum) {
            pq.emplace(entry.second, entry.first);

            // 如果堆的大小超过 k,弹出堆顶元素(最小频率)
            if (pq.size() > k) {
                pq.pop();
            }
        }
         // 从堆中取出元素,构成结果向量
        vector<int> result(k);
        int i = k-1;
        while (!pq.empty()) {
            result[i]=pq.top().second;
            pq.pop();
            i--;
        }
        return result;

    }
};
/*这段代码实际上使用了最小堆来维护前 k 高的频率元素。首先,通过哈希表 valToFreq 统计每个元素的频率。然后,使用一个最小堆 pq,将频率和元素对加入堆中。如果堆的大小超过 k,则弹出堆顶元素,以保持堆中始终只有前 k 高的频率元素。*/

方法二:

  1. 统计频率: 遍历给定的整数数组,使用一个哈希表记录每个元素出现的频率。

  2. 构建数据结构: 创建一个向量,将哈希表中的每个元素及其频率存储为键值对(pair)并添加到向量中。

  3. 构建最大堆: 通过构建一个最大堆来找到频率最高的 k 个元素。最大堆的根节点具有最大的频率。

  4. 填充最大堆: 遍历向量中的每个键值对,将它们添加到最大堆中。如果堆的大小超过 k,弹出堆顶元素,以保持堆中只有前 k 高的频率元素。

  5. 提取结果: 从最大堆中弹出 k 个元素,这些元素就是出现频率前 k 高的元素。

  6. 反转结果: 因为最大堆保证了堆顶元素是频率最高的元素,但是问题要求按任意顺序返回结果,所以最后我们需要将结果向量反转。

class Solution {
public:
    // 最大堆化函数,用于维护堆的性质
    void heapMax(vector<pair<int, int>>& uniqueNum, int n, int i) {
        int leftChild = 2 * i + 1;
        int rightChild = 2 * i + 2;
        int maxChild = i;
        
        // 如果左子节点存在且比当前节点的频率大,则更新最大子节点
        if (leftChild < n && uniqueNum[leftChild].second > uniqueNum[maxChild].second) {
            maxChild = leftChild;
        }
        // 如果右子节点存在且比当前节点的频率大,则更新最大子节点
        if (rightChild < n && uniqueNum[rightChild].second > uniqueNum[maxChild].second) {
            maxChild = rightChild;
        }
        
        // 如果最大子节点不是当前节点,则交换节点并递归调整
        if (maxChild != i) {
            swap(uniqueNum[i], uniqueNum[maxChild]);
            heapMax(uniqueNum, n, maxChild);
        }
    }

    // 构建最大堆函数
    void buildHeap(vector<pair<int, int>>& uniqueNum) {
        int n = uniqueNum.size();
        int last_node = n - 1;
        int parent = last_node / 2;
        
        // 从最后一个非叶子节点开始向上构建最大堆
        for (int i = parent; i >= 0; i--) {
            heapMax(uniqueNum, n, i);
        }
    }

    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> freqNums;
        
        // 统计每个元素的频率
        for (int i = 0; i < nums.size(); i++) {
            freqNums[nums[i]]++;
        }
        
        vector<pair<int, int>> uniqueNum;
        
        // 将频率和元素值存储为 pair 放入 uniqueNum 中
        for (const auto& entry : freqNums) {
            uniqueNum.push_back(make_pair(entry.first, entry.second));
        }
        
        // 构建最大堆
        buildHeap(uniqueNum);
        
        vector<int> result;
        int n = uniqueNum.size();
        
        // 从堆中取出前 k 高频元素
        for (int i = n - 1; i >= n - k; i--) {
            result.push_back(uniqueNum[0].first);
            swap(uniqueNum[0], uniqueNum[i]);
            heapMax(uniqueNum, i, 0);
        }
        
        return result;
    }
};

 

posted on 2023-08-25 15:48  月光下·溫渘  阅读(26)  评论(0)    收藏  举报