アルゴリズム-文字の出現頻度に従ってソート
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'被认为是两种不同的字符。
この質問は、セルフハッシュの優れたケースです。ハッシュテーブルの性質を使用して、時間の複雑さをO(N)に最適化できます。
主なアイデアは、カウンター配列を作成することです。配列インデックスは文字の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};
}
実行速度も非常に速いですが、現在、私以外のリートコードの最速は3msで、アルゴリズムは2msに達しています。
执行用时 :2 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗 :40.3 MB, 在所有 Java 提交中击败了11.11%的用户
実際、このアルゴリズムは最適化できます。2つのポイントが指摘されています
。1。関数呼び出しには呼び出しスタックのオーバーヘッドがあるため、サブ関数を使用する代わりに、毎回最大値が検索される場所をループ本体に配置できます
。2。この方法時間の複雑さは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 count=0;
char c=0;
//下面的for循环,时间复杂度为O(1),因为最坏情况下遍历次数为'z'=122次
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);
}