Medium
题目描述
给定一个字符串 s,根据字符出现的频率对其进行降序排序。一个字符出现的频率是它在字符串中出现的次数。
返回已排序的字符串。如果有多个答案,返回其中任何一个都可以。
示例 1:
输入: s = "tree"
输出: "eert"
解释: 'e' 出现两次,'r' 和 't' 都只出现一次。
因此 'e' 必须出现在 'r' 和 't' 之前。因此 "eetr" 也是一个有效的答案。
示例 2:
输入: s = "cccaaa"
输出: "aaaccc"
解释: 'c' 和 'a' 都出现了三次,所以 "cccaaa" 和 "aaaccc" 都是有效的答案。
注意 "cacaca" 是不正确的,因为相同的字母必须放在一起。
示例 3:
输入: s = "Aabb"
输出: "bbAa"
解释: "bbaA" 也是一个有效答案,但 "Aabb" 是不正确的。
注意 'A' 和 'a' 被认为是两个不同的字符。
提示:
1 <= s.length <= 5 * 10^5s由大小写英文字母和数字组成
解题思路
这道题的核心思路是统计字符频率并按频率排序。有三种常见解法:
解法1:哈希表+排序(推荐)
- 使用哈希表统计每个字符的出现频率
- 将字符-频率对放入数组,按频率降序排序
- 根据排序后的结果构建答案字符串
解法2:桶排序
- 统计字符频率后,使用桶排序的思想
- 创建频率桶,每个桶存储具有相同频率的字符
- 从高频率桶向低频率桶遍历构建结果
解法3:优先队列 使用最大堆来维护字符按频率的排序,每次取出频率最高的字符。
第一种解法最直观且效率较好,适合大多数情况。桶排序在字符种类较少时有优势,但需要额外的桶空间。优先队列解法代码简洁但有额外的堆操作开销。
代码实现
class Solution {
public:
string frequencySort(string s) {
unordered_map<char, int> freq;
for (char c : s) {
freq[c]++;
}
vector<pair<char, int>> charFreq;
for (auto& p : freq) {
charFreq.push_back({p.first, p.second});
}
sort(charFreq.begin(), charFreq.end(), [](const pair<char, int>& a, const pair<char, int>& b) {
return a.second > b.second;
});
string result;
for (auto& p : charFreq) {
result += string(p.second, p.first);
}
return result;
}
};
class Solution:
def frequencySort(self, s: str) -> str:
from collections import Counter
freq = Counter(s)
sorted_chars = sorted(freq.items(), key=lambda x: x[1], reverse=True)
result = []
for char, count in sorted_chars:
result.append(char * count)
return ''.join(result)
public class Solution {
public string FrequencySort(string s) {
var freq = new Dictionary<char, int>();
foreach (char c in s) {
freq[c] = freq.GetValueOrDefault(c, 0) + 1;
}
var charFreq = freq.ToList();
charFreq.Sort((a, b) => b.Value.CompareTo(a.Value));
var result = new StringBuilder();
foreach (var pair in charFreq) {
result.Append(new string(pair.Key, pair.Value));
}
return result.ToString();
}
}
var frequencySort = function(s) {
const freq = {};
for (const char of s) {
freq[char] = (freq[char] || 0) + 1;
}
const charFreq = Object.entries(freq);
charFreq.sort((a, b) => b[1] - a[1]);
let result = '';
for (const [char, count] of charFreq) {
result += char.repeat(count);
}
return result;
};
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 哈希表+排序 | O(n + k log k) | O(k) |
| 桶排序 | O(n + k) | O(n) |
| 优先队列 | O(n + k log k) | O(k) |
其中 n 是字符串长度,k 是不同字符的数量(最多128个ASCII字符)。
相关题目
. Top K Frequent Elements (Medium)
. Node With Highest Edge Score (Medium)
. Most Frequent Even Element (Easy)