[路飞]_前端算法第八十三弹-根据字符出现频率排序

128 阅读1分钟

给定一个字符串,请将字符串里的字符按照出现的频率降序排列。

示例 1:

输入:
"tree"

输出:
"eert"

解释:
'e'出现两次,'r''t'都只出现一次。
因此'e'必须出现在'r''t'之前。此外,"eetr"也是一个有效的答案。

示例 2:

输入:
"cccaaa"

输出:
"cccaaa"

解释:
'c'和'a'都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。

示例 3:

输入:
"Aabb"

输出:
"bbAa"

解释:
此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意'A'和'a'被认为是两种不同的字符。

这道题可以用大顶堆来实现,先将字符串中的字符统计数量存入map中,然后用大顶堆排序,最后依次输出堆顶元素

var frequencySort = function (s) {
    let heap = new Heap((a, b) => a.num < b.num)
    let map = new Map()
    let str = ""
    for (let i = 0; i < s.length; i++) {
        map.set(s[i], map.has(s[i]) ? map.get(s[i]) + 1 : 1)
    }
    map.forEach((num, key) => {
        heap.push({
            num,
            key
        })
    })
    while (heap.size()) {
        let top = heap.pop()
        for (let i = 0; i < top.num; i++) {
            str += top.key
        }
    }
    return str
};

堆的一般实现方法

class Heap {
    constructor(cmp = "large") {
        if (cmp == "large") {
            this.cmp = this.large;
        } else if (cmp == "small") {
            this.cmp = this.small
        } else {
            this.cmp = cmp
        }
        this.res = [];
        this.cnt = 0;
    }

    push(val) {
        this.cnt++;
        this.res.push(val)
        this.shiftUp(this.cnt - 1)
    }

    pop() {
        this.cnt--;
        const res = this.res[0]
        const pop = this.res.pop()
        if (this.cnt) {
            this.res[0] = pop
            this.shiftDown(0)
        }
        return res
    }

    shiftUp(i) {
        if (i === 0) return
        const par = this.getParentIndex(i)
        if (this.cmp(this.res[par], this.res[i])) {
            this.swap(par, i)
            this.shiftUp(par)
        }
    }

    shiftDown(i) {
        const l = this.getLeftIndex(i)
        const r = this.getRightIndex(i)
        if (l < this.cnt && this.cmp(this.res[i], this.res[l])) {
            this.swap(i, l)
            this.shiftDown(l)
        }
        if (r < this.cnt && this.cmp(this.res[i], this.res[r])) {
            this.swap(i, r)
            this.shiftDown(r)
        }
    }

    getParentIndex(i) {
        return (i - 1) >> 1
    }

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

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

    large = (a, b) => a < b

    small = (a, b) => a > b;

    swap = (i, j) => [this.res[i], this.res[j]] = [this.res[j], this.res[i]];

    top = () => this.res[0];

    size = () => this.cnt;

    isEmpty = () => this.cnt === 0

}