给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序。
前K个高频单词,还是可以使用堆排序: 最大堆。
- 统计单词出现的次数
- 所有单词及数量放入堆中排序:
- 如果出现次数不一致,用数量最比较,大的排在前面。用数量相减即可。
- 如果出现次数一致,则用字典序排序。js中可以使用
localeCompare。
代码层主要是 最大堆的实现以及compare的逻辑。
class MaxBinaryHeap {
constructor(data, compare) {
this.s = []
this.len = 0
this.compare = compare
this.heapify(data)
}
heapify(data) {
for (let i = 0; i < data.length; i++) {
this.push(data[i])
}
}
push(el) {
this.s[this.len] = el
++this.len
if (this.len > 1) {
this.swim(this.len - 1)
}
}
pop() {
if (this.len === 0) {
return null
}
const top = this.s[0]
if (this.len > 1) {
this.s[0] = this.s[this.len - 1]
this.sink(0)
}
this.len--
return top
}
peek() {
if (this.len === 0) {
return null
} else {
return this.s[0]
}
}
sink(idx) {
const lastIdx = this.len - 1
while (idx <= lastIdx) {
const rightIdx = (idx + 1) * 2
const leftIdx = rightIdx - 1
if (leftIdx <= lastIdx) {
let largerValueIdx = leftIdx
if (rightIdx <= lastIdx && this._compare(rightIdx, largerValueIdx) > 0) {
largerValueIdx = rightIdx
}
if (this._compare(largerValueIdx, idx) > 0) {
this.swap(idx, largerValueIdx)
idx = largerValueIdx
} else {
break
}
} else {
break
}
}
}
swim(idx) {
while (idx >= 0) {
let parent = (idx - 1) >> 1
if (parent >= 0 && this._compare(parent, idx) < 0) {
this.swap(parent, idx)
idx = parent
} else {
break
}
}
}
swap(a, b) {
let temp = this.s[a]
this.s[a] = this.s[b]
this.s[b] = temp
}
_compare(a, b) {
return this.compare(this.s[a], this.s[b])
}
peek() {
return this.s[0]
}
}
var topKFrequent = function(words, k) {
let map = new Map()
// 统计次数
for(let i = 0; i < words.length; i++) {
map.set(words[i], (map.get(words[i]) || 0) + 1)
}
// 大顶堆
const m_bp = new MaxBinaryHeap(Array.from(map.entries()), (a, b) => {
const a1 = a[0], a2 = a[1]
const b1 = b[0], b2 = b[1]
// 数量不同,以数量为主
if (a2 !== b2) {
return a2 - b2
} else {
// 数量相同就看字典序
return b1.localeCompare(a1)
}
})
const res = []
while(k--) {
const top = m_bp.pop()
res.push(top[0])
}
return res
};