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.
分析:这个题目还是蛮有意思的,题目表述的也非常简洁,就是按照字符串中字符出现频率由高到低输出一个新的字符串。思路还是比较清晰的,用一个Hashmap存一下每个字符出现的次数,key就是出现的字符,value就是次数。关键就是怎么对values从大到小并且还要取出key。这里我的思路是把values放到一个数组里,然后用Arrays.sort方法直接排序,就得到了从大到小的value。然后对每个value,再去遍历map.get(key)。代码如下:
1 class Solution { 2 public String frequencySort(String s) { 3 Map<Character,Integer> map = new HashMap<>(); 4 List<Character> list = new ArrayList<>(); 5 for ( char c : s.toCharArray() ) 6 map.put(c,map.getOrDefault(c,0)+1); 7 int[] frequency = new int[map.values().size()]; 8 int cur = 0; 9 for ( int t : map.values() ) 10 frequency[cur++] = t; 11 Arrays.sort(frequency); 12 StringBuilder res = new StringBuilder(""); 13 for ( int i = frequency.length - 1 ; i >= 0 ; i -- ){ 14 for ( char key : map.keySet() ){ 15 if ( map.get(key) == frequency[i] && !list.contains(key)) { 16 for ( int z = 0 ; z < frequency[i] ; z++ ) res.append(key); 17 list.add(key); 18 } 19 } 20 } 21 return res.toString(); 22 } 23 }
时间复杂度介于O(n^2)到O(n^3)之间,运行时间为19ms,击败了89.43%的人。
因为代码时间复杂度比较高,还有很多可以优化的空间。首先map可以用一个int[128]数组替换,这个前面已经说到过了;然后过程还可以再优化一下。代码如下:
1 class Solution { 2 public String frequencySort(String s) { 3 int[] map = new int[128]; //map数组下标与出现次数对应 4 int[] counter = new int[128]; //counter数组用来排序用的。不对应 5 for ( char c : s.toCharArray() ) { 6 map[c]+=1; 7 counter[c]+=1; 8 } 9 Arrays.sort(counter); //对counter数组排序 10 StringBuilder sb = new StringBuilder(); 11 for ( int i = counter.length - 1 ; i >= 0 ; i -- ){ //对字符出现次数从大到小遍历 12 if ( counter[i] != 0 ){ 13 int count = counter[i]; 14 for ( int j = 0 ; j < map.length ; j ++ ){ 15 if ( map[j] == count ){ 16 for ( int k = 0 ; k < count ; k ++ ) sb.append((char) j); 17 map[j] = 0; //如果找到一个字符,就将它出现的次数置为0。 18 } 19 } 20 } 21 } 22 return sb.toString(); 23 } 24 }
上面代码的关键就是生成两个数组map和counter。两者都保存着字符出现的次数,不同的是map下标和出现次数是对应的,而counter只是为了从大到小遍历出现的次数。其他的过程与上面一种方法思路类似。
运行时间6ms,击败了99.95%。表明map还是比较浪费时间的,以后遇到以char做key的map,都尽量使用int[128]数组来代替map,效率很高。