时间复杂度为线性的排序算法:
- 计数排序
- 桶排序
- 基数排序
算法思想
把排序工作拆分成多个阶段,每一个阶段只根据一个字符进行计数排序,一共排序k轮(k是字符串的长度)。
使用的计数排序必须是稳定排序,这样才能保证第1轮排出的先后顺序在第2轮还能继续保持。
从高位优先进行排序(Most Significant Digitfirst,简称MSD)
从低位优先进行排序(Least SignificantDigit first,简称LSD)
对齐问题
以最长的字符串为准,其他长度不足的字符串,在末尾补0即可。
在排序时,我们把字符0当作比a更小的字符
JAVA实现
//ascii码的取值范围
public static final int ASCII_RANGE = 128;
public static String[] radixSort(String[] array, int maxLength) {
//排序结果数组,用于存储每一次按位排序的临时结果
String[] sortedArray = new String[array.length];
//从个位开始比较,一直比较到最高位
for (int k = maxLength - 1; k >= 0; k--) {
//计数排序的过程,分成三步:
//1.创建辅助排序的统计数组,并把待排序的字符对号入座,
//这里为了代码简洁,直接使用ascii码范围作为数组长度
//count数组相当于计数排序里面的统计数组(下标代表字母对应的ascll码)
int[] count = new int[ASCII_RANGE];
for (int i = 0; i < array.length; i++) {
int index = getCharIndex(array[i], k);
count[index]++;
}
//2.统计数组做变形,后面的元素等于前面的元素之和
// (这个是优化计数排序的过程,如果遇到相同的原始,就按照原始的位置输出)
for (int i = 1; i < count.length; i++) {
count[i] = count[i] + count[i - 1];
}
//3.倒序遍历原始数列,从统计数组找到正确位置,输出到结果数组
for (int i = array.length - 1; i >= 0; i--) {
int index = getCharIndex(array[i], k);
int sortedIndex = count[index] - 1;
sortedArray[sortedIndex] = array[i];
count[index]--;
}
//下一轮排序需要以上一轮的排序结果为基础,因此把结果复制给array
array = sortedArray.clone();
}
return array;
}
//获取字符串第k位字符所对应的ascii码序号
private static int getCharIndex(String str, int k) {
//如果字符串长度小于k,直接返回0,相当于给不存在的位置补0
if (str.length() < (k + 1)) {
return 0;
}
return str.charAt(k);
}
测试方法:
public static void main(String[] args) {
String[] array = { "qd", "abc", "qwe", "hhh", "a", "cws", "ope" };
System.out.println(Arrays.toString(radixSort(array, 3)));
}
时间复杂度:O(k(n+m))
k是字符串最大长度,m是字符范围。
空间复杂度:O(n+m)