算法-根據字符出現頻率排序
1、根據字符出現頻率排序
給定一個字符串,請將字符串裏的字符按照出現的頻率降序排列。
示例 1:
輸入:
"tree"
輸出:
"eert"
解釋:
'e'出現兩次,'r'和't'都只出現一次。
因此'e'必須出現在'r'和't'之前。此外,"eetr"也是一個有效的答案。
示例 2:
輸入:
"cccaaa"
輸出:
"cccaaa"
解釋:
'c'和'a'都出現三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正確的,因爲相同的字母必須放在一起。
示例 3:
輸入:
"Aabb"
輸出:
"bbAa"
解釋:
此外,"bbaA"也是一個有效的答案,但"Aabb"是不正確的。
注意'A'和'a'被認爲是兩種不同的字符。
本題是一個自hash的絕佳案例,完全可以利用Hash表的性質將時間複雜度優化爲O(N)
主要思想是建立一個counter數組,數組索引爲字符的ASCII碼值,數組內容爲字符出現的次數,然後我們每次都去搜索數組中最大的元素,搜索完成後將最大元素的出現次數置爲0(記憶化搜索),由於字符處於0-128之間,我們查找時間複雜度實際爲O(1).因此,總時間複雜度爲O(N)。
public String frequencySort(String s) {
if(s==null){
return s;
}
int[] counter=new int[128];//自hash,統計字符出現次數
char[] cs=s.toCharArray();
for(char c:cs){
counter[c]++;
}
int curr=0;
while (curr<cs.length){
int[] result=findMax(counter);//尋找出現次數最多的元素,返回元素和元素出現次數,時間複雜度O(128)也就是O(1)
char c=(char) result[0];//找到的元素
int count=result[1];//元素出現次數
while (count-->0){
cs[curr++]=c;//填充數據
}
}
return new String(cs);
}
public int[] findMax(int[] counter){
int count=0;
int index=0;
for(int i=0;i<='z';i++){
if(counter[i]>count){
count=counter[i];
index=i;
}
}
counter[index]=0;//使用完了,下次不再使用了
return new int[]{index,count};
}
執行的速度也是極快的,目前leetcode除我之外,最快速度是3ms,而我的算法達到了2ms
執行用時 :2 ms, 在所有 Java 提交中擊敗了100.00%的用戶
內存消耗 :40.3 MB, 在所有 Java 提交中擊敗了11.11%的用戶
其實本算法還有可以優化的地方,指出兩點:
1、函數調用有調用棧開銷,因此可以將每次尋找最大值的地方放到循環體裏面去,而不是使用一個子函數
2、本方法時間複雜度雖然爲O(N),但是其實還是可以改進的,我們只對出現過的字符進行判斷就好了。
針對第1點,可以做如下優化
public String frequencySort(String s) {
if(s==null){
return s;
}
int[] counter=new int[128];//自hash,統計字符出現次數
char[] cs=s.toCharArray();
for(char c:cs){
counter[c]++;
}
int curr=0;
while (curr<cs.length){
int count=0;
char c=0;
for(int i=0;i<='z';i++){
if(counter[i]>count){
count=counter[i];
c=(char)i;
}
}
counter[c]=0;//使用完了,下次不再使用了
while (count-->0){
cs[curr++]=c;//填充數據
}
}
return new String(cs);
}