手摸手提桶跑路——LeetCode451. 根据字符出现频率排序

190 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第24天,点击查看活动详情

题目描述

给定一个字符串 s ,根据字符出现的 频率 对其进行 降序排序 。一个字符出现的 频率 是它出现在字符串中的次数。

返回 已排序的字符串 。如果有多个答案,返回其中任何一个。

示例 1:

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

示例 2:

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

示例 3:

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

解题思路

这道题没什么特别的,题意也很直接,就是按照字符串中字母出现的频率升序排序,出现次数最多的字母优先输出。

具体步骤如下:

  1. 我们可以创建一个对象 mp 来记录每个单词的出现次数。
  2. 遍历字符串,统计每个字母的出现次数。
  3. 最后通过 Object.entries 将对象 mp 转为键值对数组,然后通过 sort 以出现频率进行降序排序。
  4. 将排序后的键值对数组,对于每项元素 [k,v],进行 k.repeat(v) 操作复制 v 次之后拼接,就是最后的结果了。

更多关于 repeat 的详细内容:String.prototype.repeat() | MDN

题解

/**
 * @param {string} s
 * @return {string}
 */
var frequencySort = function (s) {
    let m = {};
    for (let w of s) {
        m[w]++ || (m[w] = 1);
    }
    const arr = Object.entries(m).sort((a, b) => b[1] - a[1])
    return arr.reduce((f, c) => {
        return f + c[0].repeat(c[1]);
    }, '');
};

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

解题思路——桶排序

桶排序需要一次遍历来统计最大的出现次数 maxCnt。然后初始化一个 length=maxCnt+1 大小的数组用来记录数据。

具体步骤如下:

  1. 初始化一个对象 mp 用来统计每个字母的出现次数。
  2. 遍历字符串,统计每个字母的出现次数,同时更新最大出现次数 maxCnt
  3. 遍历结束后初始化一个桶,为 length = maxCnt + 1二维数组
  4. 遍历 mp 对象,以每个字母的出现次数为下标,将该字母 push 到桶对应下标的数组中。
  5. 最后得到一个二维数组,其中每个元素都是一个数组,每个元素的下标表示当前下标对应的数组中的所有元素的出现次数。
  6. 统计结果。

题解

var frequencySort = function (s) {
    const mp = {};
    let maxFreq = 0;
    for (const ch of s) {
        mp[ch]++ || (mp[ch] = 1);
        maxFreq = Math.max(maxFreq, mp[ch]);
    }
    const buckets = new Array(maxFreq + 1).fill(0).map(() => new Array());
    for (const [ch, num] of Object.entries(mp)) {
        buckets[num].push(ch);
    }

    return buckets.reduce((f, c, i) => {
        return c.reduce((f2, c2) => {
            return c2.repeat(i) + f2;
        }, '') + f;
    }, '');
};

QQ截图20220816004727.png