「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」
题目介绍
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
示例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) {
const map = new Map()
for(let i = 0; i < s.length; i++) {
if(!map.has(s[i])) {
map.set(s[i], 1)
} else {
map.set(s[i], map.get(s[i]) + 1)
}
}
// map.entries() 可以返回 [[key, value], [key, value]...]
const arr = [...map.entries()]
arr.sort((a, b) => b[1] - a[1])
let ans = ''
for(let i = 0; i < arr.length; i++) {
// 每个字符的出现了多少次,就在新字符串中拼接多少个字符
while(arr[i][1]--) {
ans += arr[i][0]
}
}
return ans
};
思路二:桶排序
桶排序同样需要先计算每个字符出现的频率,不同于思路一的是,在统计每个字符出现频率的过程中,需要记录频率出现的最大值 max,然后定义一个长度为 max + 1 的数组 bucket(桶),然后将每个字符放置到下标与对应字符出现频率相同的位置上,之后再遍历整个桶,将字符按下标频率拼接到新字符串上即可
解题代码
var frequencySort = function(s) {
// 还是先利用 Map 存储每个字符出现的频率,同时记录频率的最大值
const map = new Map()
let max = 0
for(let i = 0; i < s.length; i++) {
if(!map.has(s[i])) {
map.set(s[i], 1)
} else {
map.set(s[i], map.get(s[i]) + 1)
}
max = Math.max(max, map.get(s[i]))
}
// 定义一个大小为 max + 1 的桶,因为大小为 max + 1,数组下标才能到 max
// 然后将桶中的每个位置都放置一个空数组,因为可能会出现频率相同的字符,需要用数组记录所有频率相同的字符
const buckets = new Array(max + 1).fill(0).map(() => new Array())
// 将字符放置到频率对应下标的数组中
for (const [key, value] of map.entries()) {
buckets[value].push(key)
}
let ans = ''
for (let i = buckets.length - 1; i > 0; i--) {
const bucket = buckets[i]
while (bucket.length) {
const ch = bucket.pop()
// 按每个字符出现的频率拼接到新字符串上
for(let j = 0; j < i; j++) {
ans += ch
}
}
}
return ans
};