一日一练:前K个高频单词

149 阅读1分钟

给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序。

K个高频单词,还是可以使用堆排序: 最大堆

  1. 统计单词出现的次数
  2. 所有单词及数量放入堆中排序:
    • 如果出现次数不一致,用数量最比较,大的排在前面。用数量相减即可。
    • 如果出现次数一致,则用字典序排序。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
};