75.前K个高频元素

python
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
#堆排序
count_dict = {}
for n in nums:
if n in count_dict:
count_dict[n]+=1
else:
count_dict[n]=1
#转(value,key)元组列表 等价items = list(count_dict.items())
#这步太厉害了
items = [(value,key) for key,value in count_dict.items()]
#小顶堆up操作,只用和父节点比较
def heap_up(heap,index):
while index>0:
father_index = (index-1)//2
if heap[index][0]<heap[father_index][0]:
heap[index],heap[father_index] = heap[father_index],heap[index]
index = father_index
else:
break
#小顶堆down操作,在两个孩子之中找到最小,与其交换
def heap_down(heap,index,size):
minNum_index = index
l = index*2+1
r = index*2+2
#用size检查孩子是否越界
if l<size and heap[minNum_index][0]>heap[l][0]:
minNum_index = l
if r<size and heap[minNum_index][0]>heap[r][0]:
minNum_index = r
if minNum_index!=index:
#交换的是元祖,而不是元祖里面的某个元素(无法交换!)
heap[minNum_index],heap[index] = heap[index],heap[minNum_index]
heap_down(heap,minNum_index,size)
def heap_push(heap,item):
heap.append(item)
heap_up(heap,len(heap)-1)
def heap_pop(heap):
if not heap:
return None
heap[0],heap[-1] = heap[-1],heap[0]
item=heap.pop()
heap_down(heap,0,len(heap))
return item
heap = []
#遍历出现次数数组
for value,key in items:
if len(heap)<k:
heap_push(heap,(value,key))
else:
if heap[0][0] < value:
heap_pop(heap)
heap_push(heap,(value,key))
return [ key for (value,key) in heap ]
用快速选择来做:
python
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
#快速选择
#与215数组中的第K个最大元素不同。这里元素会重复出现,需要去重,并且快速排序是根据元素的频率来排序该元素的。基准元素左边的元素频率大于基准元素的频率,基准元素右边的元素频率小于左边基准元素的频率
freq = {}
for n in nums:
if n in freq:
freq[n]+=1
else:
freq[n]=1
# 提取所有唯一元素(待筛选)
#这样写就会变成二维列表elements = [freq.keys()] 是错误的 会得到[[]]
#写成这样可以 elements = [n for n in freq]取key 如果是取value [value for value in freq.values()]
elements = list(freq.keys())
n = len(elements)
if n<=k:
return elements
def quick_select(elements,freq,k,l,r):
if l>=r:
return
x = elements[l]
x_freq = freq[x]
i,j=l-1,r+1
while i<j:
i+=1
while freq[elements[i]]>x_freq:
i+=1
j-=1
while freq[elements[j]]<x_freq:
j-=1
if i<j:
elements[i],elements[j] = elements[j],elements[i]
if j >= k-1:
quick_select(elements,freq,k,l,j)
else:
quick_select(elements,freq,k,j+1,r)
quick_select(elements,freq,k,0,len(elements)-1)
return elements[:k]
这是豆包写的堆解法,注释丰富,也可以当做手写堆排序的模板
python
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
# 统计每个元素的出现次数
count_dict = {}
for num in nums:
count_dict[num] = count_dict.get(num, 0) + 1
# 转换为(次数, 元素)的列表
items = [(freq, num) for num, freq in count_dict.items()]
# 小顶堆的辅助函数:上浮操作(插入时维护堆性质)
def heapify_up(heap, index):
while index > 0:
parent_index = (index - 1) // 2
# 若当前节点的次数小于父节点,交换两者(小顶堆特性)
if heap[index][0] < heap[parent_index][0]:
heap[index], heap[parent_index] = heap[parent_index], heap[index]
index = parent_index
else:
break # 无需继续上浮
# 小顶堆的辅助函数:下沉操作(弹出堆顶后维护堆性质)
def heapify_down(heap, index, size):
while True:
left_child = 2 * index + 1
right_child = 2 * index + 2
smallest = index # 初始化最小值索引为当前节点
# 找到左、右孩子中次数最小的节点
if left_child < size and heap[left_child][0] < heap[smallest][0]:
smallest = left_child
if right_child < size and heap[right_child][0] < heap[smallest][0]:
smallest = right_child
# 若最小节点不是当前节点,交换并继续下沉
if smallest != index:
heap[index], heap[smallest] = heap[smallest], heap[index]
index = smallest
else:
break # 无需继续下沉
# 向堆中插入元素
def heappush(heap, item):
heap.append(item)
heapify_up(heap, len(heap) - 1) # 插入后上浮维护堆
# 从堆中弹出堆顶元素(最小值)
def heappop(heap):
if not heap:
return None
# 交换堆顶与最后一个元素,便于弹出
heap[0], heap[-1] = heap[-1], heap[0]
item = heap.pop() # 弹出原堆顶
# 对新堆顶执行下沉维护堆
heapify_down(heap, 0, len(heap))
return item
# 筛选前k个高频元素
heap = []
for freq, num in items:
if len(heap) < k:
# 堆未满,直接插入
heappush(heap, (freq, num))
else:
# 堆已满,若当前元素次数大于堆顶,替换堆顶
if freq > heap[0][0]:
heappop(heap)
heappush(heap, (freq, num))
# 提取堆中的元素(前k个高频元素)
return [num for (freq, num) in heap]
简单的调包做法:
python
class Solution(object):
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
#dict key是num,value是出现次数;对dict的value进行排序(查怎么操作),然后输出倒数K个dict的key
#这样最差的时间复杂度是o(n^2)
dict = {}
for num in nums:
if num in dict:
dict[num] += 1
else:
dict[num] = 1
sorted_dict = sorted(dict.items(), key=lambda x: x[1], reverse=True)
ans = []
for i in range(k):
ans.append(sorted_dict[i][0])
return ans
#堆排序。
#一个直观的做法是 对出现次数进行堆排序。可是要怎么建立次数到元素的映射呢?用草稿纸写一下:针对想到的数据结构都去模拟
自己实现其中的快速排序:
python
class Solution(object):
def quick_sort(self,arr,l,r):
if l >= r:
return
x = arr[l][1]
i,j = l-1,r+1
while i<j:
i+=1
while arr[i][1]<x:
i+=1
j-=1
while arr[j][1]>x:
j-=1
if i<j:
#直接交换元组。这样是错的: arr[i][1],arr[j][1] = arr[j][1],arr[i][1]
arr[i],arr[j] = arr[j],arr[i]
self.quick_sort(arr,l,j)
self.quick_sort(arr,j+1,r)
def topKFrequent(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
#dict key是num,value是出现次数;对dict的value进行排序(查怎么操作),然后输出倒数K个dict的key
#这样最差的时间复杂度是o(n^2)
dict = {}
for num in nums:
if num in dict:
dict[num] += 1
else:
dict[num] = 1
dict = list(dict.items())
self.quick_sort(dict,0,len(dict)-1)
ans = []
for i in range(len(dict)-1,len(dict)-1-k,-1):
ans.append(dict[i][0])
return ans