Skip to content

Approach 1 Naive Solution

自己想出来的naive解,O(nlogn) in time, O(n) in space.

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        # hashmap to record: 
        #      val:occurence
        # O(n*logn) in time, O(n) in space
        hashtable = collections.defaultdict(int)
        for num in nums:
            hashtable[num] += 1

        sorted_hashtable = sorted(hashtable,key=lambda x:hashtable[x],reverse = True)

        return sorted_hashtable[:k]

Approach 2 Using bucket sort

Neetcode的解法 O(n) in time, O(n) in space. 比较巧妙是,用了bucket sort的思想,把频率作为index,然后把频率相同的元素放在一个list里面。这样就可以直接从高频率开始取元素了.

Tip

为什么bucket sort算法会快呢? 本质上是把一个unbounded problem转化为bounded problem. 你的输入array nums = [1,2,...1000000] 里的value的范围非常广,但是你的频率的范围则永远和len(nums)一样。也就是你有个size 6的array, 频率次数只可能是1-6. 所以你可以把这个unbounded problem转化为bounded problem.

这个思路和2-sum实际上是一个非常comparable, 对比如下,

算法复杂度有点搞脑子,假设你的input array is size n. 那么每种元素最多出现n次,所以最多有n种不同的频率。所以最多有n个bucket。所以最多有n个list. 但这n个list里,一共有的elements数量也为n个,每一个都visit一遍。所以总的时间复杂度为O(n).

Note

bucket sort的精髓在于bucket的定义, 这个定义完全依赖于你需要求的问题. 比如这个问题是求频率最高的k个元素,那么bucket的定义就是频率。如果是求最大值,那么bucket的定义就是value的范围,具体如下图.

下面的例子用了list of list的方式,但是其实用defaultdict也是可以的。

With list of list

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        # bucket sort, O(n) in time, O(n) in space

        # cost O(n) time 
        hashtable = collections.defaultdict(int)
        for num in nums:
            hashtable[num] += 1
        # cost O(n) space
        freq = [[] for _ in range(len(nums)+1)]

        # cost O(n) time
        for num,count in hashtable.items():
            freq[count].append(num)

        # cost O(n), since we gonna have x vals, where x == len(nums)
        res = []
        for count in range(len(freq)-1, 0, -1):
            # on average, we have O(1) time to get the value
            # 平均下来每个frequency一个元素
            for val in freq[count]:
                res.append(val)                
                if len(res) == k:
                    return res

with defaultdict

下面是用defaultdict的例子

from collections import defaultdict
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        hashmap = defaultdict(int)
        for num in nums:
            hashmap[num] += 1

        bucket = defaultdict(list)
        for val,freq in hashmap.items():
            bucket[freq].append(val)

        # traverse from len(nums) to 1
        res = []
        for freq in range(len(nums), 0, -1):
            for n in bucket[freq]:
                res.append(n)
                if len(res) == k:
                    return res