一、题目
Given a string, sort it in decreasing order based on the frequency of characters.
Example 1:
Input: "tree" Output: "eert" Explanation: 'e' appears twice while 'r' and 't' both appear once. So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer.
Example 2:
Input: "cccaaa" Output: "cccaaa" Explanation: Both 'c' and 'a' appear three times, so "aaaccc" is also a valid answer. Note that "cacaca" is incorrect, as the same characters must be together.
Example 3:
Input: "Aabb" Output: "bbAa" Explanation: "bbaA" is also a valid answer, but "Aabb" is incorrect. Note that 'A' and 'a' are treated as two different characters.题意:题目很长题意很简单。给定一个字符串,按照字母出现的频率由大到小输出。
注意:后边的举例就是一直在强调的,字符串的题注意事项,字符集的范围,大小写,返回的顺序,空字符。
该题范围就是大小写字母并且大小写有别,对于出现频率次数相同的串返回顺序任意。
思路:1.统计字符串中各个字母出现的频率存储在map中。
2.对map按照频率由大到小排序,这里注意是map中按照值排序。
第一反应是利用stl中提供的sort算法实现,这个想法是好的,不幸的是,sort算法有个限制,利用sort算法只能对序列容器进行排序,就是线性的(如vector,list,deque)。map也是一个集合容器,它里面存储的元素是pair,但是它不是线性存储的(前面提过,像红黑树),所以利用sort不能直接和map结合进行排序。需要将map转为vector。
参考博客:http://www.cnblogs.com/lakeone/p/5599047.html
3.对排序后的map取键并相应的频次-1。
class Solution {
public:
static bool isless(const pair<char,int> l, const pair<char,int> r) //静态成员函数???
{
return l.second > r.second;
}
string frequencySort(string s) {
map<char,int> strMap;
for(auto ch:s)
{
strMap[ch]++;
}
vector<pair<char, int>> vecMap(strMap.begin(), strMap.end()); //map按值排序
sort(vecMap.begin(), vecMap.end(), isless);
string outstr;
for (auto temp:vecMap)
{
while (temp.second)
{
outstr += temp.first; //map取键
temp.second--;
}
}
return outstr;
}
};
二、总结。
使用二元谓词的时候必须得用静态成员函数。关于静态成员函数记录两个博客:
http://blog.csdn.net/morewindows/article/details/6721430
http://blog.csdn.net/kerry0071/article/details/25741425/
至于为啥使用静态成员函数,请查看
http://blog.csdn.net/xiaxzhou/article/details/71440464
可以使用lamda表达式也很方便。
sort(vecMap.begin(), vecMap.end(), [](const pair<char, int> l, const pair<char, int> r){return l.second > r.second; }/*isless*/);
三、思路2
思路:一个直观的思路是先统计每个字符的数量,然后将这些字符连同频率放入一个优先队列中,再取出来即可.这种时间复杂度为O(n) + O(m log m),其中n为字符串长度,m为不同字符的个数,在最坏情况下时间复杂度为O(n log n),即所有字符都不一样.
注意优先队列的使用,包含头文件<queue>
string frequencySort(string s) {
map<char, int> strMap;
for (auto ch : s)
{
strMap[ch]++;
}
priority_queue<pair<int, char>> priQue;
for (auto tempMap:strMap)
{
priQue.push(make_pair(tempMap.second, tempMap.first));
}
string outstr;
while(!priQue.empty())
{
pair<int, char> temp = priQue.top();
outstr.append(temp.first, temp.second);
priQue.pop();
}
return outstr;
}
其实我们还有一种可以优化的方法,在统计完字符频率之后利用类似与计数排序的方法,开一个n+1长度大小的数组,将不同的频率字符放到频率的索引处.然后从高到低取得所有字符串.这种方法的好处是在最环情况下依然可以保证时间复杂度为O(n).
string frequencySort(string s) {
unordered_map<char, int> freq;
vector<string> bucket(s.size() + 1, "");
string res;
//count frequency of each character
for (char c : s) freq[c]++;
//put character into frequency bucket
for (auto& it : freq) {
int n = it.second;
char c = it.first;
bucket[n].append(n, c); //bucket中加入n个c
}
//form descending sorted string
for (int i = s.size(); i > 0; i--) {
if (!bucket[i].empty())
res.append(bucket[i]);
}
return res;
}
回顾知识点:最小堆
既然出现从大到小打印,那么就有可能是从小到大打印,除了将map转成vector,调用默认的sort函数排序之外,还有就是优先队列是按照最小堆建立的。
对于内置类型的最大堆,最小堆建立方法:
//默认最大堆的建立 打印输出89,67,36,23,12,4,1
vector<int> num = { 4, 23, 67, 12, 1, 36, 89 };
priority_queue<int> test1(num.begin(), num.end());
while (!test1.empty())
{
cout << test1.top() << " ";
test1.pop();
}
cout << endl;
//最小堆的建立包含<functional>,声明方式的改变,输出1,4,12,23,36,67,89
priority_queue<int, vector<int>, greater<int>> test(num.begin(), num.end());
while (!test.empty())
{
cout << test.top()<<" ";
test.pop();
}
也可使用multiset<int,greater<int>>,按照key由大到小排序
cout << endl;
multiset<int, greater<int>> myset(num.begin(), num.end());
for_each(myset.begin(), myset.end(), [](const int& out){cout << out << " "; });
完整的最小堆,从小到大频率输出字符
struct cmp
{
bool operator()(const pair<int, char>&l, const pair<int, char>&r)
{
return l.first > r.first;
}
};
string frequencySort(string s) {
map<char, int> strMap;
for (auto ch : s)
{
strMap[ch]++;
}
//注意定义的方式,同时自定义第三个函数
priority_queue<pair<int, char>, vector<pair<int, char>>, cmp> priQue;
for (auto tempMap : strMap)
{
priQue.push(make_pair(tempMap.second, tempMap.first));
}
string outstr;
while (!priQue.empty())
{
pair<int, char> temp = priQue.top();
outstr.append(temp.first, temp.second);
priQue.pop();
}
return outstr;
return 0;
}