451. 根据字符出现频率排序

254 阅读1分钟

题目描述

leetcode-cn.com/problems/so…

题目分析

/**
 * @param {string} s
 * @return {string}
 */
var frequencySort = function(s) {

};

题目只给了一个参数 s,要求返回的是一个排序之后的字符串

解题思路

本题要根据频率排序,所以我先把频率用一个对象存起来,然后用堆将对象中所有键排序,最后从堆中不断地弹出元素,重新构建一个新的字符串返回

算法,数据结构

堆:按频率存储字符(注意是存储不同的字符,而不是 s 中的所有字符) 对象:存储字符的频率

过程

声明变量

声明对象 freq,堆 h(堆的实现过程此处忽略),堆的主要方法有:

extract:弹出堆顶元素
insert:插入一个元素
isEmpty:检查堆是否为空

统计频率

遍历字符串,统计每个字符频率:

for (const x of s) {
    if (freq[x]) {
      freq[x]++
    } else {
      freq[x] = 1
    }
  }

拿到每个字符的频率

堆排序,插入每个字符

然后,想堆中 insert 字符: 这一步需要特别注意,插入的是 Object.keys(freq),而不是 s 中的所有字符

做完上面的插入之后,堆中所有字符按照频率已经排好,接下来构建字符串

构建字符串

从堆中不断地弹出元素,并从 freq 查找他的频率,最后构建字符串

代码

/**
 * @param {string} s
 * @return {string}
 */
var frequencySort = function (s) {
  const freq = {}
  const h = new Heap((a, b) => freq[a] <= freq[b])
  for (const x of s) {
    if (freq[x]) {
      freq[x]++
    } else {
      freq[x] = 1
    }
  }
  for (const x of Object.keys(freq)) {
    h.insert(x)
  }

  let ret = ""
  while (!h.isEmpty()) {
    const c = h.extract()
    let f = freq[c]

    while (f--) {
      ret += c
    }
  }

  return ret
}

class Heap {
  constructor(compreFn) {
    this.compreFn = compreFn
    this.heap = []
  }

  getLeftIndex(index) {
    return index * 2 + 1
  }

  getRightIndex(index) {
    return index * 2 + 2
  }

  getParentIndex(index) {
    return Math.floor((index - 1) / 2)
  }

  size() {
    return this.heap.length
  }

  isEmpty() {
    return this.heap.length === 0
  }

  swap(parent, index) {
    const arr = this.heap
    ;[arr[parent], arr[index]] = [arr[index], arr[parent]]
  }

  insert(value) {
    const index = this.heap.length
    this.heap.push(value)

    this.siftUp(index)
  }

  siftUp(index) {
    let parent = this.getParentIndex(index)

    while (index > 0 && this.compreFn(this.heap[parent], this.heap[index])) {
      this.swap(parent, index)
      index = parent
      parent = this.getParentIndex(index)
    }
  }

  extract() {
    if (this.isEmpty()) return
    if (this.size() === 1) return this.heap.pop()

    const removedItem = this.heap[0]
    this.heap[0] = this.heap.pop()
    this.siftDown(0)

    return removedItem
  }

  siftDown(index) {
    let element = index
    const left = this.getLeftIndex(index)
    const right = this.getRightIndex(index)

    if (
      index < this.size() &&
      this.compreFn(this.heap[element], this.heap[left])
    ) {
      element = left
    }

    if (
      index < this.size() &&
      this.compreFn(this.heap[element], this.heap[right])
    ) {
      element = right
    }

    if (index !== element) {
      this.swap(element, index)
      this.siftDown(element)
    }
  }

  top() {
    return this.heap[0]
  }
}