【Python3】 LeetCode 7月挑战题目之17 - Top K Frequent Elements

第十七天问:Top K Frequent Elements

这次,题目会先给出一个只包含整数的列表,然后要写一个函数来找出首k个重复最多次数的数字。在有时间复杂度限制的条件下,就必须用小小手法来完成。

大家好,我是一个喜欢研究算法、机械学习和生物计算的小青年,我的CSDN博客是:一骑代码走天涯
如果您喜欢我的笔记,那么请点一下关注、点赞和收藏。如果內容有錯或者有改进的空间,也可以在评论让我知道。😄

题目&示例 (引用自 LeetCode)

按此进入题目链结

Given a non-empty array of integers, return the k most frequent elements.

Example 1:

Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]

Example 2:

Input: nums = [1], k = 1
Output: [1]

Note:

  1. You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
  2. Your algorithm’s time complexity must be better than O(n log n), where n is the array’s size.
  3. It’s guaranteed that the answer is unique, in other words the set of the top k frequent elements is unique.
  4. You can return the answer in any order.

解题思路

Python自带了一个哈希表 (Hashmap) 的类別,或者又称为dict,可以让这题轻松解決掉。我们只需要遍历这列表一次,把每个数字的出现次数都记録在dict里,然后就能照着里面的次数记録,找出首k个出现次数最多的数字。

代码 (直接遍历)

时间复杂度:O( ​ n log ⁡ n ​n \log n nlogn)
空间复杂度:O( n n n)

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        freq_dict = {}
        # Time complexity = O(n)
        for i in nums:
            if i not in freq_dict:
                freq_dict[i] = 1
            else:
                freq_dict[i] += 1
        # Time complexity = O(n log n)
        ordered = sorted(freq_dict.items(), key=lambda x:x[1], reverse=True)
        res = [ordered[i][0] for i in range(k)]
        return res 

另外,力扣 (LeetCode) 的官方答案中,有一个方法是用了 快速选择 (Quick Select, 又称Hoare’s selection algorithm) 来解決这道问题。虽然我並不了解这个算法,但这算法应该是非常常用的,而且是课本式算法,所以也在这里分享一下。

from collections import Counter
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        count = Counter(nums)
        unique = list(count.keys())
        
        def partition(left, right, pivot_index) -> int:
            pivot_frequency = count[unique[pivot_index]]
            # 1. move pivot to end
            unique[pivot_index], unique[right] = unique[right], unique[pivot_index]  
            
            # 2. move all less frequent elements to the left
            store_index = left
            for i in range(left, right):
                if count[unique[i]] < pivot_frequency:
                    unique[store_index], unique[i] = unique[i], unique[store_index]
                    store_index += 1

            # 3. move pivot to its final place
            unique[right], unique[store_index] = unique[store_index], unique[right]  
            
            return store_index
        
        def quickselect(left, right, k_smallest) -> None:
            """
            Sort a list within left..right till kth less frequent element
            takes its place. 
            """
            # base case: the list contains only one element
            if left == right: 
                return
            
            # select a random pivot_index
            pivot_index = random.randint(left, right)     
                            
            # find the pivot position in a sorted list   
            pivot_index = partition(left, right, pivot_index)
            
            # if the pivot is in its final sorted position
            if k_smallest == pivot_index:
                 return 
            # go left
            elif k_smallest < pivot_index:
                quickselect(left, pivot_index - 1, k_smallest)
            # go right
            else:
                quickselect(pivot_index + 1, right, k_smallest)
         
        n = len(unique) 
        # kth top frequent element is (n - k)th less frequent.
        # Do a partial sort: from less frequent to the most frequent, till
        # (n - k)th less frequent element takes its place (n - k) in a sorted array. 
        # All element on the left are less frequent.
        # All the elements on the right are more frequent.  
        quickselect(0, n - 1, n - k)
        # Return top k frequent elements
        return unique[n - k:]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值