Leetcode 692. 前K个高频单词 (hashmap + 最小堆 + __lt__(self, other)魔法方法 + collections.Counter()
(hashmap + 最小堆 + lt(self, other)魔法方法 + collections.Counter() )
理念:
- 词频倒序排列 大的在前
- 若词频相同,按字母顺序排序 正序排列 字典序小的在前
解法1 hashmap + 最小堆 + lt(self, other)魔法方法 + collections.Counter()
时间复杂度O(NlogK)
空间复杂度O(N)
def __lt__(self, other): # 改成什么"<"就是按照什么排序
def __lt__(self, other): # 改成什么"<"就是按照什么排序
def __lt__(self, other): # 改成什么"<"就是按照什么排序
定义一个名为Word的自定义类,它包含了key和value两个属性,分别表示单词和出现次数。
此外,该类还重写了__lt__方法,该方法指定了对象的比较方式,以确保在进行排序时,按照出现次数从小到大排序,如果出现次数相同时,则按照单词从大到小排序。
需求主要是为了定义除数字型外的实例对比过程,
Python的富比较方法包括__lt__
、__gt__
分别表示:小于
、大于
,对应的操作运算符为:“<”
、“>”
具体来说,__lt__方法定义了一个other参数,该参数是指其他要比较的对象。方法首先检查两个对象的出现次数,如果它们相等,则将两个对象的单词按字母降序排列,否则,将两个对象的出现次数按升序排列。在进行排序时,通常使用Python的内置sorted函数或list对象的sort方法,它们会自动调用类的__lt__方法,以便在排序期间比较对象。
class Word:
def __init__(self, key, value): # 改成什么就是按照什么排序
self.key = key
self.value = value
def __lt__(self, other): # 重写小于号< ,如果二者的值相等,就定义 小于 为key的大于,实现词频倒序,
if self.value == other.value:
return self.key > other.key # 表示value相等时,按照key 就是按照单词从大到小排序
else:
return self.value < other.value # 表示value不等时,按照value 就是出现的次数从小到大排序
例如,如果有一个表示学生的类,想要根据学生的平均分对学生进行排序,那么可以重写__lt__方法,该方法应该返回一个布尔值,指示考虑两个对象时第一个对象是否小于第二个对象。
具体来说,重写__lt__方法的代码通常需要检查两个对象的某些属性,并根据这些属性的值来决定它们的顺序。如果第一个对象小于第二个对象,则该方法应该返回True;否则返回False。
例如,如果您的Student类包含名字和平均分属性,则可以重写__lt__方法如下:
class Student:
def __init__(self, name, avg_grade):
self.name = name
self.avg_grade = avg_grade
def __lt__(self, other):
return self.avg_grade < other.avg_grade
在此示例中,__lt__方法使用学生的平均分进行比较。如果一个学生的平均分小于另一个学生的平均分,则该方法返回True,并表示第一个学生应该排在第二个学生之前。
接下来首先将字符和其出现的次数遍历到hash表中,有两种方法:
★方法一:使用colloections.Counter()
hash = collections.Counter(words)
方法二:遍历到hash
hash = {}
for word in words:
if word not in hash:
hash[word] = 0
hash[word] += 1
★接下来就初始化一个堆,之后使用for key,value in hash.items()
遍历hash
heap = []
for key,value in hash.items():
★之后使用heapq.heappush
函数,将元素添加到堆中的适当位置。
具体来说,是先添加新元素到堆的末尾,之后使用其中的规则维护堆的构造
★可以使用自定义类和__lt__方法
来更改最小堆的排序方式。当我将一个自定义类对象添加到堆中时,heapq模块会自动调用对象的__lt__方法来确定该对象与堆中其他对象的相对顺序。
heapq.heappush(heap, Word(key, value))
之后我们设计只要堆中的元素超过k,就按照规则pop出去heap顶(最小堆顶的元素就是小的,留下的就是大的)元素
最后把堆内元素遍历出来,使用reverse
翻转,即为正确结果。
全部代码如下:
class Solution:
def topKFrequent(self, words: List[str], k: int) -> List[str]:
import heapq
hash = {}
for word in words:
if word not in hash:
hash[word] = 0
hash[word] += 1
# 以上的功能相当于hash = collections.Counter(words)
heap = [] #初始化一个堆
for key,value in hash.items(): # 遍历hash
heapq.heappush(heap, Word(key, value)) # heapq.heappush作用是将一个元素添加到堆中的适当位置
if len(heap) >k: #堆内元素大于k 就按照规则(就是Word中建立的规则)弹出
heapq.heappop(heap)
res = []
while len(heap)>0: # 将key逐个弹出进列表输出
temp = heapq.heappop(heap)
res.append(temp.key)
res.reverse()
return res
解法2 hashmap+collections.Counter+ 多关键字排序 sorted(hash, key = lambda x:(-hash[x], x))
时间复杂度O(NlogN)
Count函数的时间复杂度为O(n),因为它需要遍历一次输入的单词列表并使用哈希表来计算每个单词出现的次数。排序函数sorted的时间复杂度为O(nlogn)
空间复杂度O(N)
class Solution:
def topKFrequent(self, words: List[str], k: int) -> List[str]:
hash = collections.Counter(words) #返回一个字典 统计每个元素出现的次数
res = sorted(hash, key=lambda x:(-hash[x], x)) # 对字典排序
# 关键字一个是负哈希value 即为次数的逆序,一个是哈希的key 即为名称的正序
return res[:k]