本页目录
347.前 K 个高频元素
题目
LeetCode 中等
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
输入: nums = [1], k = 1
输出: [1]
题解
方法:map + sort
O(nlogn) O(map 的长度)
js
function topKFrequent(nums, k) {
// 统计出现次数
const map = new Map()
const len = nums.length
for (let i = 0; i < len; i++) {
const val = nums[i]
map.set(val, (map.get(val) || 0) + 1)
}
console.log(map)
// 对次数进行排序然后输出前 k 个
return [...map.entries()]
.sort((a, b) => b[1] - a[1])
.slice(0, k)
.map(a => a[0])
}
const nums = [1, 1, 1, 2, 2, 3],
k = 2
console.log('🌰', topKFrequent(nums, k))
执行结果
Map(3) { 1 => 3, 2 => 2, 3 => 1 }
🌰 [ 1, 2 ]
方法:堆排序
O(nlogk) O(n)
js
function topKFrequentHeap(nums, k) {
//计算次数
let nlen = nums.length
const map = new Map()
for (let i = 0; i < nlen; i++) {
const val = nums[i]
map.set(val, (map.get(val) || 0) + 1)
}
console.log(map)
//以map建堆
const list = [...map.entries()]
let heapSize = list.length
let llen = list.length
buildMaxHeap(list, heapSize)
console.log(
'init maxHeap',
list.map(a => a.toString()),
)
//把堆顶下沉,并重新调整堆,一步步重复
for (let i = llen - 1; i >= llen - k; i--) {
swap(list, 0, i)
--heapSize
maxHeapify(list, 0, heapSize)
console.log(
i,
list.map(a => a.toString()),
)
}
return list.slice(-k).map(a => a[0])
// 自下而上构建一颗大顶堆
function buildMaxHeap(list, heapSize) {
for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
maxHeapify(list, i, heapSize)
}
}
// 从左向右,自上而下的调整节点
function maxHeapify(list, i, heapSize) {
let l = i * 2 + 1
let r = i * 2 + 2
let largest = i
if (l < heapSize && list[l][1] > list[largest][1]) {
largest = l
}
if (r < heapSize && list[r][1] > list[largest][1]) {
largest = r
}
if (largest !== i) {
swap(list, i, largest)
maxHeapify(list, largest, heapSize)
}
}
//交换
function swap(arr, i, j) {
;[arr[i], arr[j]] = [arr[j], arr[i]]
}
}
const nums = [1, 10, 3, 8, 10, 9, 1, 4],
k = 2
console.log('🌰', topKFrequentHeap(nums, k))
执行结果
Map(6) { 1 => 2, 10 => 2, 3 => 1, 8 => 1, 9 => 1, 4 => 1 }
init maxHeap [ '1,2', '10,2', '3,1', '8,1', '9,1', '4,1' ]
5 [ '10,2', '4,1', '3,1', '8,1', '9,1', '1,2' ]
4 [ '9,1', '4,1', '3,1', '8,1', '10,2', '1,2' ]
🌰 [ 10, 1 ]
方法:快排的优化版
O(n) O(n)
js
function topKFrequentQuickSort(nums, k) {
const map = new Map()
const len = nums.length
for (let i = 0; i < len; i++) {
const val = nums[i]
map.set(val, (map.get(val) || 0) + 1)
}
const list = [...map.entries()]
return quickSort(0, list.length - 1).map(a => a[0])
function quickSort(left, right) {
console.log(list.map(i => i.toString()))
if (list.length <= 1) return list
let pivotIndex = left //选择的中值
let pivotNum = list[pivotIndex][1]
console.log(
'left',
left,
'right',
right,
'k',
k,
'pivotIndex',
pivotIndex,
'pivot',
list[pivotIndex],
)
if (left < right) {
//与中值比较,大的往前放,小的往后放
for (let i = left + 1; i <= right; i++) {
if (list[i][1] >= pivotNum) {
// 碰到大于等于的值,先前进一步
pivotIndex++
/**
* i > pivotIndex 就是往后push的意思
* i === pivotIndex ,当前和前一个交换
* */
if (i > pivotIndex) {
;[list[pivotIndex], list[i]] = [list[i], list[pivotIndex]]
} else {
;[list[pivotIndex - 1], list[i]] = [list[i], list[pivotIndex - 1]]
}
}
}
console.log(
'after sort, pivotIndex',
pivotIndex,
list.map(a => a.toString()),
)
console.log('------')
}
if (pivotIndex === k - 1) {
return list.slice(0, k)
} else if (pivotIndex > k) {
return quickSort(left, pivotIndex)
} else {
return quickSort(pivotIndex + 1, right)
}
}
}
const nums = [1, 10, 7, 3, 8, 1, 7, 1, 3],
k = 3
// const nums = [1, 1, 1, 2, 2, 3],
// k = 2
const res = topKFrequentQuickSort(nums, k)
console.log('🌰', res)
执行结果
[ '1,3', '10,1', '7,2', '3,2', '8,1' ]
left 0 right 4 k 3 pivotIndex 0 pivot [ 1, 3 ]
after sort, pivotIndex 0 [ '1,3', '10,1', '7,2', '3,2', '8,1' ]
------
[ '1,3', '10,1', '7,2', '3,2', '8,1' ]
left 1 right 4 k 3 pivotIndex 1 pivot [ 10, 1 ]
after sort, pivotIndex 4 [ '1,3', '7,2', '3,2', '8,1', '10,1' ]
------
[ '1,3', '7,2', '3,2', '8,1', '10,1' ]
left 1 right 4 k 3 pivotIndex 1 pivot [ 7, 2 ]
after sort, pivotIndex 2 [ '1,3', '3,2', '7,2', '8,1', '10,1' ]
------
🌰 [ 1, 3, 7 ]