目录/Table of Content
第十七天问: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:
- You may assume k is always valid, 1 ≤
k
≤ number of unique elements. - Your algorithm’s time complexity must be better than O(n log n), where n is the array’s size.
- It’s guaranteed that the answer is unique, in other words the set of the top k frequent elements is unique.
- 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:]