给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
提示:
1 <= nums.length <= 105
k 的取值范围是 [1, 数组中不相同的元素的个数]
题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的
进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/top-k-frequent-elements
python
# 0347.前k个高频元素
# https://leetcode-cn.com/problems/top-k-frequent-elements/solution/dai-ma-sui-xiang-lu-347-qian-kge-gao-pin-efgx/
# 时间复杂度:O(nlogk)
# 空间复杂度:O(n)
import heapq
class Solution2:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
# 要统计元素出现频率
map_ = {} # nums[i]:对应出现的次数
for i in range(len(nums)):
map_[nums[i]] = map_.get(nums[i], 0) + 1
# 对频率排序
# 定义一个小顶堆,大小为k
pri_que = [] # 小顶堆
# 用固定大小为k的小顶堆,扫面所有频率的数值
for key, freq in map_.items():
heapq.heappush(pri_que, (freq, key))
if len(pri_que) > k: # 如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
heapq.heappop(pri_que)
# 找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒叙来输出到数组
result = [0] * k
for i in range(k - 1, -1, -1):
result[i] = heapq.heappop(pri_que)[1]
return result
# https://leetcode-cn.com/problems/top-k-frequent-elements/solution/python-dui-pai-xu-by-xxinjiee/
class Solution:
# 小顶堆-力扣超时!
def topKFrequent(self, nums: [int], k: int) -> [int]:
def sift_down(arr, root, k):
"""下沉logk, 如果新的根节点>子节点就一直下沉"""
val = arr[root]
while root << 1 < k:
child = root << 1
# 选取左右孩子中小的父节点交换
if child | 1 < k and arr[child[1][1]] < arr[child][1]:
child |= 1
# 如果子节点<新节点,交换, 如果有序break
if arr[child][1] < val[1]:
arr[root] = arr[child]
root = child
else:
break
arr[root] = val
def sift_up(arr, child):
"""上浮logk, 如果新加入的节点<父节点就一直上浮"""
val = arr[child]
while child >> 1 > 0 and val[1] < arr[child>>1][1]:
arr[child] = arr[child>>1]
child>>1
arr[child] = val
from collections import Counter
stat = Counter(nums)
stat = list(stat.items())
heap = [(0, 0)]
# 构建规模为k+1的堆,新元素加入堆尾,上浮
for i in range(k):
heap.append(stat[i])
sift_up(heap, len(heap)-1)
# 维护规模为k+1的堆,如果新元素大于堆顶,入堆并下沉
for i in range(k, len(stat)):
if stat[i][1] > heap[1][1]:
heap[1] = stat[i]
sift_down(heap, 1, k+1)
return [item[0] for item in heap[1:]]
# 大顶堆 堆排序
def heapSort(arr):
"""
从后往前非叶子节点下沉,依次向上保证每一个子树都是大顶堆,构造大顶锥
依次把大顶堆根节点与尾部节点交换(不再维护,堆规模 -1),新根节点下沉。
:param arr:
:return:
"""
def sift_down(arr, root, k):
val = arr[root]
while root << 1 < k:
child = root << 1
if child|1 < k and arr[child|1] > arr[child]:
child |= 1
if arr[child] > val:
arr[root] = arr[child]
root = child
else:
break
arr[root] = val
arr = [0] + arr
k = len(arr)
for i in range((k-1)>>1, 0, -1):
sift_down(arr, i, k)
for i in range(k-1, 0, -1):
arr[1], arr[i] = arr[i], arr[1]
sift_down(arr, 1, i)
return arr[1:]
golang
//方法一:小顶堆
func topKFrequent(nums []int, k int) []int {
map_num:=map[int]int{}
//记录每个元素出现的次数
for _,item:=range nums{
map_num[item]++
}
h:=&IHeap{}
heap.Init(h)
//所有元素入堆,堆的长度为k
for key,value:=range map_num{
heap.Push(h,[2]int{key,value})
if h.Len()>k{
heap.Pop(h)
}
}
res:=make([]int,k)
//按顺序返回堆中的元素
for i:=0;i<k;i++{
res[k-i-1]=heap.Pop(h).([2]int)[0]
}
return res
}
//构建小顶堆
type IHeap [][2]int
func (h IHeap) Len()int {
return len(h)
}
func (h IHeap) Less (i,j int) bool {
return h[i][1]<h[j][1]
}
func (h IHeap) Swap(i,j int) {
h[i],h[j]=h[j],h[i]
}
func (h *IHeap) Push(x interface{}){
*h=append(*h,x.([2]int))
}
func (h *IHeap) Pop() interface{}{
old:=*h
n:=len(old)
x:=old[n-1]
*h=old[0:n-1]
return x
}
//方法二:利用O(logn)排序
func topKFrequent(nums []int, k int) []int {
ans:=[]int{}
map_num:=map[int]int{}
for _,item:=range nums {
map_num[item]++
}
for key,_:=range map_num{
ans=append(ans,key)
}
//核心思想:排序
//可以不用包函数,自己实现快排
sort.Slice(ans,func (a,b int)bool{
return map_num[ans[a]]>map_num[ans[b]]
})
return ans[:k]
}
作者:carlsun-2
链接:https://leetcode-cn.com/problems/top-k-frequent-elements/solution/dai-ma-sui-xiang-lu-347-qian-kge-gao-pin-efgx/