LeetCode #347 前K个高频元素 堆排序 桶排序

LeetCode #347 前K个高频元素

题目描述

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

示例 1:

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

示例 2:

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

说明:

  • 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
  • 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。

方法一:hashmap + 堆排序

用 hashmap 维护元素出现的频率,堆排序得到最大的 k,不过我觉得这样调用库函数会被面试官打死。。。

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        hashmap = {
     }
        for i in nums:
            if i in hashmap:
                hashMap[i] += 1
            else:
                hashmap[i] = 1
        
        return heapq.nlargest(k, hashMap.keys(), key=hashMap.get)

官方写法,我。。。。。。

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        count = collections.Counter(nums)   
        return heapq.nlargest(k, count.keys(), key=count.get) 

正常写法

class Solution:
	def topKFrequent(self, nums: List[int], k: int) -> List[int]:
		hashmap = {
     }
		for i in nums:
			if i in hashmap:
				hashmap[i] += 1
			else:
				hashmap[i] = 1

		# 维护一个小顶堆
		heap = []
		for key in hashmap:
			# heap中存储的是 (值,键) 元组,这样堆就会根据值排序
			if len(heap) < k:
				heapq.heappush(heap, (hashmap[key], key))
			else:
				if hashmap[key] > heap[0][0]:
					heapq.heapreplace(heap, (hashmap[key], key))
    	# 因为是小顶堆,所以要反序输出
		ans = []
		for i in heap:
			ans.insert(0, i[1])
		return ans
  • 时间复杂度: O ( N l o g ( k ) ) O(Nlog(k)) O(Nlog(k)):hashmap 的方法复杂度是 O ( N ) O(N) O(N),建堆和输出的复杂度是 O ( N l o g ( k ) ) O(Nlog(k)) O(Nlog(k)),所以总复杂度是 O ( N + N l o g ( k ) ) = O ( N l o g ( k ) ) O(N+Nlog(k))=O(Nlog(k)) O(N+Nlog(k))=O(Nlog(k))
  • 空间复杂度: O ( N ) O(N) O(N),hashmap的存储开销

方法二:hashmap + 桶排序

前面的一样,也是用 hashmap 统计数量。接下来做一个键值对的反转,将值当作下标存入数组中,下标对应的值就是真正的数,这样通过下标的方式读出最高频的 k 个数。需要注意的是,可能有些数出现的频率是相同的,这样数组的元素应该是可变的列表,实现来自评论区 @程序员小吴
LeetCode #347 前K个高频元素 堆排序 桶排序_第1张图片

//基于桶排序求解「前 K 个高频元素」
class Solution {
     
    public List<Integer> topKFrequent(int[] nums, int k) {
     
        List<Integer> res = new ArrayList();
        // 使用字典,统计每个元素出现的次数,元素为键,元素出现的次数为值
        HashMap<Integer,Integer> map = new HashMap();
        for(int num : nums){
     
            if (map.containsKey(num)) {
     
               map.put(num, map.get(num) + 1);
             } else {
     
                map.put(num, 1);
             }
        }
        
        //桶排序
        //将频率作为数组下标,对于出现频率不同的数字集合,存入对应的数组下标
        List<Integer>[] list = new List[nums.length+1];
        for(int key : map.keySet()){
     
            // 获取出现的次数作为下标
            int i = map.get(key);
            if(list[i] == null){
     
               list[i] = new ArrayList();
            } 
            list[i].add(key);
        }
        
        // 倒序遍历数组获取出现顺序从大到小的排列
        for(int i = list.length - 1;i >= 0 && res.size() < k;i--){
     
            if(list[i] == null) continue;
            res.addAll(list[i]);
        }
        return res;
    }
}

作者:MisterBooo
链接:https://leetcode-cn.com/problems/top-k-frequent-elements/solution/leetcode-di-347-hao-wen-ti-qian-k-ge-gao-pin-yuan-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 时间复杂度: O ( N ) O(N) O(N):hashmap 的时间复杂度为 O ( N ) O(N) O(N),桶的数量为 N + 1 N+1 N+1,所以桶排序的时间复杂度为 O ( N + N + 1 ) = O ( N ) O(N+N+1)=O(N) O(N+N+1)=O(N)
  • 空间复杂度: O ( N ) O(N) O(N)

你可能感兴趣的:(LeetCode,#,排序,#,哈希表,leetcode,排序算法,堆排序,算法)