各种经典排序算法网上还是比较多的,但是此处实现一个时间复杂度为O(n)(不完全准确,有一定条件)的排序算法。借助JAVA的BitSet来实现,仅提供一个思路。
废话不多说,直接上代码:
public static void sort(){ int[] arr = new int[]{32,21,7,29,18,50}; int max = max(arr); System.out.println(max); BitSet bitSet = new BitSet(max); //初始化bitSet for(int i=0;i<bitSet.length();i++){ bitSet.set(i,false); } for(int j=0;j<arr.length;j++){ bitSet.set(arr[j],true); } int i=0; for(int k=0;k<bitSet.length();k++){ if(bitSet.get(k)) arr[i++] = k; } System.out.print(Arrays.toString(arr)); } private static int max(int[] arr){ int max = 0; for(int item:arr){ if(item>max){ max = item; } } return max; }
借助BitSet位存储来对应到具体的某一个排序的值,当然借助别的形式也可以实现,比如new int[] 数组,其长度同样为待排序数组的最大值,但是这样是有限制的,int的最大值是Integer.MAX_VALUE(2147483647),最小值是Integer.MIN_VALUE(-2147483648)。一般情况下是够用。关键是占用空间问题,一个int是4个字节,32位,所以如果指定int的长度,则会需要更大的存储空间。假设指定int长度为5,则存储需要20个字节(相当于640位)。而BitSet指定长度后,是按照位进行数据存储,这样数据就会少很多。所以BitSet常用于存储海量数据。
BitSet可以存储海量数据,但是同时,BitSet会过滤重复的内容,因为它是以true/false来标志的,所以要是对于重复的内容就不能直接使用了,如果非要可能具有相同内容进行排序,可以用以下的方式来实现。
思路:存储某个值出现几次,需要一个存储KV的数据结构。这里使用concurrentHashMap来实现。后面会说明原因。废话不多说,依旧直接上代码
public static void sort(){ int[] arr = new int[]{2,1,7,6,2,3,5,2,6}; Map<Integer,Integer> count = new ConcurrentHashMap<Integer,Integer>(arr.length); #增加一个Map对象存储 int max = max(arr); System.out.println(max); BitSet bitSet = new BitSet(max); //初始化bitSet,默认为false,可以不设置 for(int i=0;i<bitSet.length();i++){ bitSet.set(i,false); } for(int j=0;j<arr.length;j++){ bitSet.set(arr[j],true); if (count.containsKey(arr[j])){ count.put(arr[j],count.get(arr[j])+1); }else{ count.put(arr[j],1); } } Set<Map.Entry<Integer,Integer>> sets = count.entrySet(); for(Map.Entry<Integer,Integer> entry:sets){ if(entry.getValue() == 1){ count.remove(entry.getKey()); #遍历Map,将只出现一次的删除掉 } } int i=0; for(int k=0;k<bitSet.length();k++){ if(bitSet.get(k)){ if(!sets.isEmpty()){ if (count.containsKey(k)){ #若Map集合包含有出现1次以上的数值,则按出现的次数进行操作 int cc = count.get(k); for(int j=0;j<cc;j++){ arr[i++] = k; } count.remove(k); #将已经处理过的移除 }else{ arr[i++] = k; } }else{ arr[i++] = k; } } } System.out.println(Arrays.toString(arr)); } private static int max(int[] arr){ int max = 0; for(int item:arr){ if(item>max){ max = item; } } return max; }
可以看到在代码23行和35行执行了map的remove操作,而且在操作之后进行又进行了读操作。由于HashMap是非线程安全的,在进行读写操作执行时,会发生异常,尤其在编码中,如果在线上项目,除非你能确定使用场景,否则一但HashMap操作不当,就会造成各种异常情况,而且可能还不太好排查。而ConcurrentHashMap是线程安全的,而且其内部的加锁机制也不是以前粗暴的加锁方式,也更优雅。有兴趣的同学可以Google下此类的分析或者直接看源码实现。