leetcode 451. Sort Characters By Frequency

257 篇文章 17 订阅

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.
这道题,我的思路就是得到字符串中每个字符出现的次数,然后从大到小排序,再拼接字符串。为了提升速度,从大到小排序我用了快排,结果发现还是超出了时间限制,非常气愤,就去看大家怎么搞的,发现大家都使用了以空间换时间的bucket sort,这个排序大部分情况下能胜过快排。然后我就照着大家的思路写了一种解法:先得到每个字符出现的次数,然后桶排序次数,桶越往后,对应的次数越大,最后从最后的桶开始由后往前拼接字符串。

按道理这样写代码,我的思路解法都跟大家的一样,最后结果也应该和大家一样AC了,可是发现还是TLE了,为啥呢,我就打了几个断点算时间,如下代码所示(是我的代码):

public class Solution {
	   public String frequencySort(String s) {		   
		    long time1=System.currentTimeMillis();                          
			String result="";
			int[] map=new int[128];
			char[] chars=s.toCharArray();
			int max=0;
			for(int i=0;i<chars.length;i++){
				map[chars[i]]++;
				max=map[chars[i]]>max?map[chars[i]]:max;
			}    
			long time2=System.currentTimeMillis();                  
			String[] bucket=new String[max+1];
			for(int i=0;i<128;i++){
				int num=map[i];
				if(num>0){
					if(bucket[num]==null){
						bucket[num]=(char)i+"";
					}
					else{
						bucket[num]=bucket[num]+(char)i;
					}
				}		
			}
			long time3=System.currentTimeMillis();                     
			for(int i=max;i>=1;i--){
				String theString=bucket[i];
				if(theString!=null){
					char[] thisChars=theString.toCharArray();
					for(int j=0;j<thisChars.length;j++){
						for(int k=0;k<i;k++){
							result=result+thisChars[j];
						}
					}			
				}
				
			}
			long time4=System.currentTimeMillis();
			float excTime1=(float)(time2-time1)/1000;
			float excTime2=(float)(time3-time2)/1000;
			float excTime3=(float)(time4-time3)/1000;
			System.out.println("执行时间1:"+excTime1+"s");
            System.out.println("执行时间2:"+excTime2+"s");
            System.out.println("执行时间3:"+excTime3+"s");
			return result;
		}
	}

然后发现excTime1和excTime2都在0.04内,只有excTime3居然飙到了1秒多,然后我看了看别人的解法:
public String frequencySort(String s) {
        if(s.length() < 3)
            return s;
        int max = 0;
        int[] map = new int[256];
        for(char ch : s.toCharArray()) {
            map[ch]++;
            max = Math.max(max,map[ch]);
        }
        String[] buckets = new String[max + 1]; // create max buckets
        for(int i = 0 ; i < 256; i++) { // join chars in the same bucket
            String str = buckets[map[i]];
            if(map[i] > 0)
                buckets[map[i]] = (str == null) ? "" + (char)i : (str + (char) i);
        }
        StringBuilder strb = new StringBuilder();
        for(int i = max; i >= 0; i--) { // create string for each bucket.
            if(buckets[i] != null)
                for(char ch : buckets[i].toCharArray())
                    for(int j = 0; j < i; j++)
                        strb.append(ch);
        }
        return strb.toString();
    }
第三部分用的是StringBuilder!原来String和StringBuilder拼接字符串性能相差是云泥之别啊!而且经过查阅资料我得知:StringBuilder比StringBuffer运行速度要快,因为StringBuilder是针对于单线程的,所以它是非线程安全的,普通情况下建议使用StringBuilder。
对于三者使用的总结: 1.如果要操作少量的数据用 = String
           2.单线程操作字符串缓冲区下操作大量数据 = StringBuilder
           3.多线程操作字符串缓冲区下操作大量数据 = StringBuffer
然后我就听话地换成了StringBuilder,果然就AC了,而且还beat了96%的java submissions...

另外,关于bucket sort,请看我下一篇转载的文章。

之后我又想到,我之前用快排TLE了,会不会也是因为String拼接太慢的原因呢?然后我换成了StringBuilder,果然AC了!并且beat了90%的java submissions,这样看来确实bucket sort比快排要快啊。使用快排的代码如下所示,其中all数组记录了所有ascii码的字符出现的次数,index为ascii码。因为快排后index会改变,所以引入一个biaoji数组来记录每个位置对应的ascii码。

public class Solution {
    public String frequencySort(String s) {
		int[] biaoji=new int[127];
		int[] all=new int[127];
		char[] charArray=s.toCharArray();
		for(int i=0;i<biaoji.length;i++){
			biaoji[i]=i;
		}
		for(int i=0;i<charArray.length;i++){
			int charIndex=charArray[i];
			all[charIndex]++;
		}
		kuaipai(all,biaoji, 0, all.length-1);
		StringBuffer sb=new StringBuffer();
		for(int i=0;i<biaoji.length;i++){
			char a=(char)(biaoji[i]);
			if(all[i]==0){
				break;
			}
			for(int j=0;j<all[i];j++){
				sb.append(a);
			}
		}
		return sb.toString();		
	}
	
	public void kuaipai(int[] all,int[] biaoji,int left,int right){
		if (left < right) {
			int low = left;
			int high = right;
			int pivot = all[low];
			int biaojipivot=biaoji[low];
			while (low < high) {
				while (low < high && all[high] <= pivot) {
					high--;
				}
				if(low<high){
					biaoji[low] = biaoji[high];
					all[low] = all[high];
					low++;
				}	
				while (low < high && all[low] >= pivot) {
					low++;
				}
				if(low<high){
					biaoji[high] = biaoji[low];
					all[high] = all[low];
					high--;
				}			
			}
			biaoji[low] = biaojipivot;
			all[low] = pivot;
			kuaipai(all, biaoji, left, low - 1);
			kuaipai(all, biaoji, high + 1, right);
		}		
	}

}

其他大神的想法类似,都是三步曲,只不过第一步换成了hashmap来统计每个字符的出现次数。
1.构建一个map,统计每个字符出现的次数。
2.创建一个数组,使得这个数组的Index来表达字符出现的个数。Build a map of characters to the number of times it occurs in the string
3.从后往前遍历该数组,在每个index处,把该位置的字符加index次到结果字符串上。

public String frequencySort(String s) {
    if (s == null) {
        return null;
    }
    Map<Character, Integer> map = new HashMap();
    char[] charArray = s.toCharArray();
    int max = 0;
    for (Character c : charArray) {
        if (!map.containsKey(c)) {
            map.put(c, 0);
        }
        map.put(c, map.get(c) + 1);
        max = Math.max(max, map.get(c));
    }

    List<Character>[] array = buildArray(map, max);

    return buildString(array);
}

private List<Character>[] buildArray(Map<Character, Integer> map, int maxCount) {
    List<Character>[] array = new List[maxCount + 1];
    for (Character c : map.keySet()) {
        int count = map.get(c);
        if (array[count] == null) {
            array[count] = new ArrayList();
        }
        array[count].add(c);
    }
    return array;
}

private String buildString(List<Character>[] array) {
    StringBuilder sb = new StringBuilder();
    for (int i = array.length - 1; i > 0; i--) {
        List<Character> list = array[i];
        if (list != null) {
            for (Character c : list) {
                for (int j = 0; j < i; j++) {
                    sb.append(c);
                }
            }
        }
    }
    return sb.toString();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值