题目
题目链接:451. 根据字符出现频率排序
给定一个字符串 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 <= s.length <= 5 * 105
s
由大小写英文字母和数字组成
解题思路
👉哈希表
可以根据题目的意思直接求解
用哈希表统计每个字符出现的频次,然后对哈希表进行从大到小排序,再逐个把每个字符按照频次连接,返回连接后的字符串。
虽然思路简单,但是具体的操作过程还是有点麻烦的,尤其对哈希表不熟悉的话就很难实现了。我认为具体的代码实现有下面几个难点:
- 遍历哈希表
- 对哈希表从大到小排序
对于哈希表里的数据类型应该都没什么问题,就是 char
和 int
型。遍历所给字符串,统计字符频次存入哈希表。然后就需要对哈希表排序,哈希表的排序需要用到一个 vector
模板,把哈希表对应的键值对放入一个模板数组中,然后对模板数组排序,后面就使用模板数组操作。
所以排序哈希表需要遍历哈希表,如果熟悉C++11,那么可以直接使用 auto
遍历。但我一直不太用的习惯它,所以我选择定义迭代器遍历。
vector<pair<char, int>> vec; //定义一个模板数组
unordered_map<char, int>::iterator iter;
for (iter = map.begin(); iter != map.end(); iter ++ ) {
vec.push_back(make_pair(iter->first, iter->second));
//将哈希表内容放入数组中。make_pair是一个转换模板的数组
}
C++没有提供模板的排序,所以模板的排序需要自己定义。不过不是完全自己定义,可以用 sort
函数,但排序规则需要自己写。sort
函数的第三个参数是一个函数,写法如下:
static bool cmp(参数) {
//必须是 static bool
内容
}
排序后即可使用该数组将字符放入新的字符串。
👉暴力
还有一种纯暴力的写法,用一个数组存储每个字符的频次,然后用双重循环按频次逐个从大到小找到每个字符。
- 第一重:遍历数组
- 第二重:找到频次最大的那个字符,结束后将字符连入字符串,同时将其频次置
0
找到字符后按照字符的频次将字符连入新的字符串。
虽然这种方法很暴力,但是我认为这种方法比第一种方法好一点。虽然是双重循环,但循环的次数最大不会超过 256
,所以双重循环的时间复杂度可以近似看成一个常数。
👉桶排序
这才是这题的正解
桶排序相当于是对方法一进行了优化,虽然也使用了哈希表,但是除去了对哈希表的排序,用一种很巧妙的思路解题。
首先还是把字符的频次放入哈希表中,同时找出最大的频次数 maxnum
。用一个大小为 maxnum
的字符串数组&#x