leetcode-pp / 91alg-9-daily-check

5 stars 0 forks source link

【Day 20 】2022-11-20 - 347. 前 K 个高频元素 #27

Open azl397985856 opened 1 year ago

azl397985856 commented 1 year ago

347. 前 K 个高频元素

入选理由

暂无

题目地址

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

前置知识

 

示例 1:

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

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

提示:

1 <= nums.length <= 10^5 k 的取值范围是 [1, 数组中不相同的元素的个数] 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的  

进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。

Yongxi-Zhou commented 1 year ago

思路

与215类似,先用map统计数字频率,用set去重后再用heapq排序,建最大堆,然后取前k个元素即可。

代码

    class Solution(object):
        def topKFrequent(self, nums, k):
            """
            :type nums: List[int]
            :type k: int
            :rtype: List[int]
            """
            m = collections.defaultdict(int)
            for num in nums:
                m[num] += 1
            s = set(nums)

            pq = []
            heapq.heapify(pq)
            while s:
                num = s.pop()
                heapq.heappush(pq, (-m[num], num))

            res = []
            for i in range(k):
                count, num = heapq.heappop(pq)
                res.append(num)
            return res

复杂度

time O(N) space O(N)

xiaomingshixiaotang commented 1 year ago

思路

  1. 通过哈希表保存每个数字以及其对应出现的次数
  2. 通过优先队列(小顶堆)保存前k个数字出现次数最多的pair(通过自己定义函数对象实现)

代码

class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int,int> cnt;
        for(int i = 0;i < nums.size();i++)
        {
            cnt[nums[i]]++;
        }

        class MyGreater
        {
        public:
            bool operator()(const pair<int,int>& p1,const pair<int,int>& p2)
            {
                return p1.second > p2.second;
            }
        };

        priority_queue<pair<int,int>,vector<pair<int,int>>,MyGreater> q;
        for(auto n : cnt)
        {
            q.push(n);
            if(q.size() > k)
            {
                q.pop();
            }
        }

        vector<int> res;
        while(!q.empty())
        {
            res.push_back(q.top().first);
            q.pop();
        }

        return res;
    }
};

复杂度分析

954545647 commented 1 year ago

思路

使用hashmap记录次数配合最小堆进行排序

代码

var topKFrequent = function (nums, k) {
  let n = nums.length;
  let map = new Map();
  for (let i = 0; i < n; i++) {
    const item = nums[i];
    const val = map.get(item);
    if (map.has(item)) {
      map.set(item, val + 1)
    } else {
      map.set(item, 1)
    }
  }

  const res = [];
  let len = 0;
  map.forEach((value, key) => {
    // 小于k个直接插入
    if (len < k) {
      res.push(key);
      if (len === k - 1){
        buildHeap(res, map, k)
      }
    } else {
      // 多的先判断该值和堆顶的大小。
      if (map.get(res[0]) < value) {
        res[0] = key;
        heapify(res,map,0,k);
      }
    }
    len++
  })

  // 构建最大堆
  function buildHeap(list, map, k) {
    const last = Math.floor(k / 2);
    for (let i = last; i >= 0; i--) {
      heapify(list, map, i, k);
    }
  }

  function swap(items, i, j) {
    let temp = items[i]
    items[i] = items[j]
    items[j] = temp
  }

  // 排序
  function heapify(list, map, i, heapSize) {
    // if (i >= heapSize) return
    const left = 2 * i + 1;
    const right = 2 * i + 2;
    let min = i;
    if (left < heapSize && map.get(list[left]) < map.get(list[min])) {
      min = left;
    }
    if (right < heapSize && map.get(list[right]) < map.get(list[min])) {
      min = right
    }
    if (min !== i) {
      swap(list, min, i);
      heapify(list,map, min, heapSize)
    }

  }

  return res
};