[路飞]前 K 个高频单词

135 阅读2分钟

记录 1 道算法题

前 k 个高频单词

leetcode-cn.com/problems/to…


这道题不可避免的要遍历两遍。第一遍统计每个单词出现的次数,第二遍进行排序。

  1. 统计次数

我们使用一个对象key的唯一性的特点,进行计数。

    const map = {}
    
    for(let item of words) {
        if (map[item]) {
            map[item]++
        } else {
            map[item] = 1
        }
    }

排序的时候如果次数相同就要比较字母的顺序,所以我们需要同时准备单词和次数。但是我们只有一个对象,所以我们要通过 Object.entries ,转化成键值对。

  1. 排序

现在我们已经转成了键值对,接下来就是排序。排序有两种方法,一种是直接排序,另一种就是堆。

  • 直接排序
    // 利用sort函数进行排序
    // 进行字母的排序则需要 String.prototype.localeCompare
    // 定制一个给 sort 用的函数, 这时 words 的 item 是键值对
    const compare = (a, b) => {
        if (a[1] === b[1]) {
            const t = a[0].localeCompare(b[0])
            // 如果大于0 证明 a 在 b 后面
            // 如果小于0 证明 a 在 b 前面
            if (t > 0) {
                return -1
            } else {
                return -1
            }
        } else {
            return a[1] - b[1]
        }
    }
    
    // 因为是升序排列的,所以要截取后面的,并且要反转过来输出。
    // 这就是结果, 语言支持的 sort,性能还是不错的。
    return words.sort(compare).slice(k * -1).reduceRight((a,b) => {
        a.push(b[0]
        return a
    }, [])

完整代码如下;

    function topKFrequent(words, k) {
        const map = {}
        for(let item of words) {
            if (map[item]) {
                map[item]++
            } else {
                map[item] = 1
            }
        }
        words = Object.entries(map)
        const compare = (a, b) => {
            if (a[1] === b[1]) {
                const t = a[0].localeCompare(b[0])
                if (t > 0) {
                    return -1
                } else if (t < 0) {
                    return 1
                } else {
                    return 1
                }
            } else {
                return a[1] - b[1]
            }
        }

        return words.sort(compare).slice(k*-1).reduceRight((a,b) => {
            a.push(b[0])
            return a
        }, [])
    }
  • 堆(优先队列)

堆的实现在另一篇文章中有介绍,这里直接使用。

完整代码如下:

    function topKFrequent(words, k) {
        const map = {}
        for (let item of words) {
          if (map[item]) {
            map[item]++
          } else {
            map[item] = 1
          }
        }
        words = Object.entries(map)
        const compare = (a, b) => {
          if (a[1] === b[1]) {
            const t = a[0].localeCompare(b[0])
            if (t > 0) {
              return -1
            } else if (t < 0) {
              return 1
            } else {
              return 1
            }
          } else {
            return a[1] - b[1]
          }
        }
        /* -------------------------- */
        // 这里开始和直接排序不同。
        const heap = new Heap(compare)

        for (let item of words) {
            // 先和堆顶的元素进行判断,不符合直接跳过
          if (heap.size() === k && compare(heap.data[0], item) > 0) {
            continue
          }
          heap.push(item)
          if (heap.size() > k) {
            heap.pop()
          }
        }
        
        // 因为堆里面是没有排序的,所以要进行一次排序
        return heap.data.sort(compare).reduceRight((a, b) => {
          a.push(b[0])
          return a
        }, [])
      }

      class Heap {
        constructor(compare) {
          this.data = []
          this.compare = compare
        }

        size() {
          return this.data.length
        }

        swap(n1, n2) {
          const { data } = this
          const temp = data[n1]
          data[n1] = data[n2]
          data[n2] = temp
        }

        push(val) {
          this.data.push(val)
          this.bubblingUp(this.size() - 1)
        }

        pop() {
          if (this.size() === 0) return null
          const { data } = this
          const discard = data[0]
          const newMember = data.pop()
          if (this.size() > 0) {
            data[0] = newMember
            this.bubblingDown(0)
          }

          return discard
        }

        bubblingUp(index) {
          while (index > 0) {
            const parent = (index - 1) >> 1
            const { data } = this
            if (this.compare(data[index], data[parent]) < 0) {
              this.swap(parent, index)
              index = parent
            } else {
              break
            }
          }
        }

        bubblingDown(index) {
          const { data } = this
          const last = this.size() - 1
          while (true) {
            const left = index * 2 + 1
            const right = index * 2 + 2
            let parent = index
            if (left <= last && this.compare(data[left], data[parent]) < 0) {
              parent = left
            }

            if (right <= last && this.compare(data[right], data[parent]) < 0) {
              parent = right
            }
            if (index !== parent) {
              this.swap(index, parent)
              index = parent
            } else {
              break
            }
          }
        }
     }

结束