字符串排序算法概述
来源:互联网 发布:cnzz数据统计 编辑:程序博客网 时间:2024/06/11 18:20
一 键索引计数法
首先针对小数组的排序方法,我们将数组中不同的字符串看做一个键r,对应键有个值r,如果需要按键值排序,那么键索引计数法就十分高效
例如,我们将学生分为若干组,要求按照组号进行排序。此处组好就是对应的键值,我们分一下四个步骤进行排序:
1 频率统计
创建一个int数组count,并计算每个键出现的频率。对于每一个字符串,使用对应的键访问count数组并将其加1。如果键为r,则将count[r + 1]加1。上述例子中统计频率后的count数组如下:
index012345value003 566可以看出,count[2]保存着第一小组的总人数,count[3]保存着第二小组的总人数,以此类推。2 将频率转化为索引
对count数组进行叠加:count[r + 1] += count[r] 便可以将频率转化为索引。上述例子中转化为索引后的count数组如下:
index012345value003 814203 数据分类
新建一个辅助数组aux,将所有字符串移动到aux中。aux的索引是字符串对应的键的count值决定的,在移动之后将count[r]加1,保证count[r]总是下一个键为r的元素在aux中的索引位置。
4 回写
将aux数组回写到a中
整个过程码如下:
/** * Created by HP on 2017/5/19. */public class Main { private static class Data<K, V> { K key; V value; public Data(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } @Override public String toString() { return String.valueOf(key); } } public static void indexBasedSort(Data<String, Integer>[] a) { int size = a.length; int R = 5; Data<String, Integer>[] aux = new Data[size]; int[] count = new int[R + 1]; // 1 频率统计 for (int i = 0; i < size; i++) { count[a[i].getValue() + 1]++; } // 2 将频率转化为索引 for (int i = 0; i < R; i++) { count[i + 1] += count[i]; } // 3 数据分类 for (int i = 0; i < size; i++) { aux[count[a[i].getValue()]++] = a[i]; } // 4 回写 for (int i = 0; i < size; i++) { a[i] = aux[i]; } } public static void main(String[] args) { Data<String, Integer>[] a = new Data[20]; a[0] = new Data<>("anderson", 2); a[1] = new Data<>("brown", 3); a[2] = new Data<>("davis", 3); a[3] = new Data<>("carcia", 4); a[4] = new Data<>("harris", 1); a[5] = new Data<>("jackson", 3); a[6] = new Data<>("johnson", 4); a[7] = new Data<>("jones", 3); a[8] = new Data<>("martin", 1); a[9] = new Data<>("martinez", 2); a[10] = new Data<>("miller", 2); a[11] = new Data<>("moore", 1); a[12] = new Data<>("robinson", 2); a[13] = new Data<>("smith", 4); a[14] = new Data<>("taylor", 3); a[15] = new Data<>("thomas", 4); a[16] = new Data<>("thompson", 4); a[17] = new Data<>("white", 2); a[18] = new Data<>("williams", 3); a[19] = new Data<>("wilson", 4); indexBasedSort(a); for (int i = 0; i < a.length; i++) { System.out.println(a[i].getKey() + " " + a[i].getValue()); } }}
二 低位优先的字符串排序
该方法从右向左检查字符串中字符并进行排序,适用于字符串长度都相等的排序应用中。如果字符串长度为W,那么就从右向左将每个字符都看成键,用键索引计数法排序。
/** * Created by HP on 2017/5/19. */public class LSD { /** * 低位优先排序 * * @param a * @param W */ public static void sort(String[] a, int W) { int size = a.length; int R = 256; String[] aux = new String[size]; for (int j = W - 1; j >= 0; j--) { int[] count = new int[R + 1]; for (int i = 0; i < size; i++) { count[a[i].charAt(j) + 1]++; } for (int i = 0; i < R; i++) { count[i + 1] += count[i]; } for (int i = 0; i < size; i++) { aux[count[a[i].charAt(j)]++] = a[i]; } System.out.println("j = " + j + ", result:"); for (int i = 0; i < size; i++) { a[i] = aux[i]; System.out.println(a[i]); } } } public static void main(String[] args) { String[] a = new String[13]; a[0] = "4PGC938"; a[1] = "2IYE230"; a[2] = "3CIO720"; a[3] = "1ICK750"; a[4] = "1OHV845"; a[5] = "4JZY524"; a[6] = "1ICK750"; a[7] = "3CIO720"; a[8] = "1OHV845"; a[9] = "1OHV845"; a[10] = "2RLA629"; a[11] = "2RLA629"; a[12] = "3ATW723"; sort(a, 7); }}
三 高位优先的字符串排序
如果字符串长度都不相同,那么应该从左向右遍历字符排序。
/** * Created by HP on 2017/5/19. * * 高位优先字符串排序 */public class MSD { private static final int R = 256; private static final int M = 15; private static String[] aux; public static int charAt(String a, int index) { if (index < a.length()) { return a.charAt(index); } return -1; } public static void sort(String[] a) { int length = a.length; aux = new String[length]; sort(a, 0, length - 1, 0); } private static void sort(String[] a, int lo, int hi, int d) { if (lo > hi) { return; } if (lo + M >= hi) { insertSort(a, lo, hi); return; } int[] count = new int[R + 2]; for (int i = lo; i <= hi; i++) { count[charAt(a[i], d) + 2]++; } for (int i = 0; i <= R; i++) { count[i + 1] += count[i]; } for (int i = lo; i <= hi; i++) { aux[count[charAt(a[i], d) + 1]++] = a[i]; } for (int i = lo; i <= hi; i++) { a[i] = aux[i - lo]; } for (int i = 0; i < R; i++) { sort(a, lo + count[i], lo + count[i + 1] - 1, d + 1); } } private static void insertSort(String[] a, int lo, int hi) { for (int i = lo; i <= hi; i++) { for (int j = i; j > 0; j--) { if (a[j].compareTo(a[j - 1]) < 0) { exange(a, j, j - 1); } } } } private static void exange(String[] a, int i, int j) { String temp = a[i]; a[i] = a[j]; a[j] = temp; } public static void main(String[] args) { String[] a = new String[13]; a[0] = "she"; a[1] = "sells"; a[2] = "seashells"; a[3] = "by"; a[4] = "the"; a[5] = "seashore"; a[6] = "the"; a[7] = "shells"; a[8] = "she"; a[9] = "sells"; a[10] = "are"; a[11] = "surely"; a[12] = "seashells"; MSD.sort(a); for (String s: a) { System.out.println(s); } }}
性能分析:
该算法采用递归来实现,性能方面有三个要注意的点
1 小型子数组
假设需要将数百万个字符串(R=256)进行排序而不处理小数组,那么每个字符串最终会产生只含有它自己的子数组,因此你要对数百万个大小为1的子数组进行排序,每次排序都要将大小为R=256 + 2 个元素进行初始化并将其转化为索引,这十分消耗时间。
2 相等的字符串
如果待排序数组中含有大量相等的字符串,那么MSD的性能将会下降,最坏情况就是所有的键都相同,这样会退化到基于低位优先的排序。
3 额外空间
由于MSD采用递归实现,每次递归都要初始化一个count数组,所以count占有的空间才是主要问题。
时间复杂度:
基于大小为R的字母表的N个字符串排序,时间复杂度为N~Nw之间,w是字符串平均长度
空间复杂度:
count数组必须在sort()方法中创建,因此控件需求总量与R和递归深度成正比(再加上辅助数组N)。而递归深度就是最长字符串的长度,故空间复杂度为 RW+N。
四 三向字符串快速排序
三向字符串快速排序可以应对含有较长公共前缀的字符串的情况。例如分析网站日志便会应用此算法。
/** * Created by HP on 2017/5/22. * * 三项切分的字符串排序 */public class Quick3String { public static void sort(String[] a) { if (a == null || a.length == 0) { return; } sort(a, 0, a.length - 1, 0); } private static void sort(String[] a, int lo, int hi, int d) { if (lo >= hi) { return; } int lt = lo; int gt = hi; int i = lo + 1; int index = charAt(a[lo], d); while (i <= gt) { int t = charAt(a[i], d); if (index < t) { exange(a, i, gt--); } else if (index > t) { exange(a, i++, lt++); } else { i++; } } sort(a, lo, lt, d); if (index > 0) { sort(a, lt + 1, gt, d + 1); } sort(a, gt + 1, hi, d); } public static int charAt(String a, int index) { if (index < a.length()) { return a.charAt(index); } return -1; } private static void exange(String[] a, int i, int j) { String temp = a[i]; a[i] = a[j]; a[j] = temp; } public static void main(String[] args) { String[] a = new String[13]; a[0] = "she"; a[1] = "sells"; a[2] = "seashells"; a[3] = "by"; a[4] = "the"; a[5] = "seashore"; a[6] = "the"; a[7] = "shells"; a[8] = "she"; a[9] = "sells"; a[10] = "are"; a[11] = "surely"; a[12] = "seashells"; Quick3String.sort(a); for (String s: a) { System.out.println(s); } }}
五 总结
六 参考资料
算法(第四版)
- 字符串排序算法概述
- 排序算法之概述
- 排序算法之概述
- 排序算法概述
- 排序算法概述
- 内部排序算法概述
- 排序算法概述
- 常见排序算法概述
- JAVA排序算法<概述>
- 概述排序算法
- 排序算法的概述
- 排序算法概述java
- 排序算法概述
- 排序算法(1):主流排序算法概述
- 数据结构与算法--排序概述
- 排序算法(一) 概述
- 排序算法总结之排序概述
- 字符串排序等算法
- NGN-android开发中的知识点(二)
- Linux常用命令
- log4j 详解
- 苹果核
- 导入Beautifulsoup 报错 AttributeError: 'module' object has no attribute '_base'
- 字符串排序算法概述
- shell——tr的用法
- React生命周期过程说明
- CocoaPods的安装
- Kotlin的那些事儿
- JS对应键盘的值
- Struts表单处理器ActionForm(静态动态)
- 包学会之浅入浅出Vue.js:开学篇
- csapp 实验 Cache Lab: Understanding Cache Memories