String之字符串排序

键索引计数法

我们先介绍一种适合小整数键的简单排序方法,这是我们将要学习的字符串排序的基础,举个例子,我们希望将全班学生按组分类。如图


姓名


An


Br


Da


Ga


Ha


Ja


Jh


Jn


Ma


组号


2


3


3


4


1


3


4


3


1


姓名


Mb


Mi


Mo


Ro


Sm


Ta


Ta


Tp


Wh


组号


2


2


1


2


4


3


4


4


2


姓名


Wl


Ws


组号


3


4

我们这里用数组a[]来存储每个元素,其中每个元素都包含=一个名字和一个组号,a[i].key()返回元素的组号。

排序后的结果


姓名


Ha


Ma


Mo


An


Mb


Mi


Ro


Wh


Br


组号


1


1


1


2


2


2


2


2


3


姓名


Da


Ja


Jn


Ta


Wl


Ga


Jh


Sm


Ta


组号


3


3


3


3


3


4


4


4


4


姓名


Tp


Ws


组号


4


4

排序共分为四个步骤:

(一)频率统计

组号在0-R之间,键为组号,用一个int数组(初始全为0)统计每个键出现的频率,如果键为r,则count[r]++,但是在实际使用中我们用count[r+1]++,至于为什么将r加1,这是为了计算方便(下一步详细说明)

我i们先来进行统计:

An在第二组中,count[2+1]加1,count[3]==1继续扫描数组,Br在第三组,count[4]+1,count[4]==1,继续扫描,Da在第三组中,count[4]+1,count[4]==2……

扫描一遍数组得到count[0]=0,count[1]=0,count[2]=3,count[3]=5,count[4]=6,count[5]=6,即键1,2,3,4出现的次数分别为3,5,6,6次。

将频率转化为索引:

用count[]来计算每个键在排序结果中的索引位置,例如,第一组有三个人,第二组有5个人,那么第三组的同学在排序结果数组中的位置一定是8。

如图:下标从0开始


1


1


1


2


2


2


2


2


3(下标为8)


……

键1下标开始count[1]=count[1]+count[0]=0

键2下标开始count[2]=count[2]+count[1]+count[0]=3.

键3下标开始count[3]=count[3]+count[2]+count[1]+count[0]=8

即对于每个键r,小于r+1的键的频率之和为小于r的键的频率之和在加上count[r]。

这样我们就能直观的看出来,计算某个键的起始位置,只需要计算count[这个键]+……count[0]即可,count[]数组的根本目的在于计算并存储索引位置,不在于存储键的频率。

数据分类

‘在将count[]数组转化为一张索引表后,我们将所有元素移到一个辅助数组aux[]中进行排序,每个元素在aux[]中的位置是由它的键(组号)决定的,在移动后将count[]中对应的元素加1,这个过程只需要遍历一遍数组即可完成。这种排序是稳定的。

for(int i=0;i<a.length;i++)

{   aux[count[a[i].key()]++]=a[i];

}

其中a[i].key()获取元素的组号,count[a[i].key()]++来保证下一个元素的索引位置。

步骤如图:

分类前:

aux[]


Count[1]


Count[2]


Count[3]


Count[4]

分类中:


1


Count[1


2


2


Count[2]


3


3


3


3


Count[3]


4


4


Count[4]

分类后:


1


1


1


2


2


2


2


2


3


3


3


3


3


3


4


4


4


4


4

其中count[]指向3,count[2]指向8……

回写:

将辅助数组中的元素移动到原数组中

相关代码:

int N=a.length;

String aux[]=new String[N];

int[] count=new int[R+1];

//计算出现的次数

for(int i=;i<N;i++)

{

count[a[i].key()+1]++;

}

//将频率转化为索引

for(int r=0;r<R;r++)

{

count[r+1]+=count[r];

}

//将元素分类

for(int i=;i<N;i++)

{

aux[count[a[i].key()]++]=a[i];

}

//回写

for(int i=;i<N;i++)

{

a[i]=aux[i];

}

低优先的字符串排序

一般称为低位优先,对于一个字符串,从右向左扫描,这个方法依赖于我们上面介绍的键索引记数法,非常适合排序定长的字符串,比如身份证,车牌号,IP地址等。

代码实现:

 1 public class LSD {
 2
 3     public static void sort(String[] a,int w)
 4     {
 5         int N=a.length;
 6         int R=256;//使用扩展的ASCII字符集
 7         String[] aux=new String[N];
 8
 9         for(int d=w-1;d>=0;d--)
10
11         {
12             int[] count=new int[R+1];
13             for(int i=0;i<N;i++)
14             {
15
16               count[a[i].charAt(d)+1]++;
17             }
18
19             for(int r=0;r<R;r++)
20             {
21                 count[r+1]+=count[r];
22             }
23
24             for(int i=0;i<N;i++)
25             {
26                 aux[count[a[i].charAt(d)]++]=a[i];
27             }
28
29             for(int i=0;i<N;i++)
30             {
31                 a[i]=aux[i];
32             }
33         }
34     }
35
36     public static void main(String[] args) {
37         String[] a= {"564","964","637","159"};
38         System.out.println(a[1].charAt(2));
39         sort(a,3);
40         for(String s:a)
41         {
42             System.out.println(s);
43         }
44     }
45
46 }

要将每个元素均为含有w个字符的字符串数组a[]排序,需要进行w次键索引计数排序;从右向左,以每个字符为键排序一次。

高位优先的字符串排序:

如果要处理的字符串的大小不同,我们应该考虑从左向右遍历所有字符,例如以a开头的字符串应该排在以b开头的字符串前面。首先用键索引计数法将所有字符串按首字母排序,然后(递归的)将每个首字符对应的子数组排序(忽略首字母,因为每个首字母都是相同的)。和快速排序一样,高位的字符会将数组切分为能够独立排序的子数组来完成排序任务,但是它产生的切分会分为每个首字母得到一个子数组,而不是像快速排序那样产生固定的两个或三个切分。

对字符串末尾的约定:

一个合理的做法是将所有字符已经被检查过的字符串所在额子数组排在所有子数组的前面。这样就不需要递归的将该子数组排序。我们使用charAt()方法接收两个参数,并将字符串中的字符索引转化为数组索引,当指定的位置超过了字符串的末尾时该方法返回-1。然后我们将所有返回值加1得到一个非负的int值并用它作为count[]的索引,即0表示字符串末尾,1表示字符串第一个字符。。。。。。所以每个字符可能产生R+1种可能的位置情况。又因为键索引计数法本来就需要一个额外的位置,所以count[]=new int[R+1+1];这里的R指的是字符串中使用的字符集所包含的字符个数,如:字符集为ASCII,则R=128;扩展ASCII,R=256,Unicode,R=65536。当然你也可以选择自定义,如果字符串里只包含英文字母的大小写,则R=26+26=52;

代码实现:

 1 public class MSD {
 2
 3      private static int R=256;//扩展ASCII
 4      private static final int M=15;//小数组的切换阈值。
 5      private static String[] aux;
 6
 7      public static void sort(String[] a)
 8      {
 9          int N=a.length;
10          aux=new String[N];
11          sort(a,0,N-1,0);
12      }
13      private static void sort(String[] a,int begin,int end,int index) {
14         if(end<begin+M) {
15             insertion(a,begin,end,index);
16             return;
17         }
18
19         int count[]=new int[R+2];
20         for(int i=begin;i<=end;i++)
21         {  int c=charAt(a[i],index);
22             count[c+2]++;
23             /*
24              * 对于键r我们是存储在count[r+1]里的,例如字符串are(假设a是字符集里的第一个字符),我们将a的频率存在count[2]
25             中,在调用charAt()方法时,chartAt("are",0)返回的结果其实是0,因为我们实际上调用的是String类里的
26             方法,数组下标从0开始,所以这里需要加2来保证正确性。
27              */
28         }
29
30         for(int r=0;r<R+1;r++)
31         {
32             count[r+1]+=count[r];
33         }
34
35         for(int i=begin;i<end;i++)
36         {
37             aux[count[charAt(a[i],index)+1]++]=a[i];
38         }
39
40         for(int i=begin;i<=end;i++)
41         {
42             a[i]=aux[i-begin];
43         }
44
45        for(int r=0;r<R;r++)
46        {
47            sort(a,begin+count[r],begin+count[r+1]-1,index+1);
48        }
49      }
50      //插入排序
51      private static void insertion(String[] a,int begin,int end,int index)
52      {
53          for(int i=begin;i<=end;i++)
54              for(int j=i;j>begin&&less(a[j],a[j-1],index);j++)
55              {
56                  exch(a,j,j-1);
57              }
58      }
59
60      private static int charAt(String s,int index)
61      {
62          if(index<s.length())
63              return s.charAt(index);
64
65          else
66              return -1;
67      }
68
69
70
71
72
73       private static void exch(String[] a, int i, int j) {
74             String temp = a[i];
75             a[i] = a[j];
76             a[j] = temp;
77         }
78
79       private static boolean less(String v,String w,int index)
80       {
81           int min=Math.min(v.length(), w.length());
82           for(int i=index;i<min;i++)
83           {
84               if(v.charAt(i)<w.charAt(i)) return true;
85               if(v.charAt(i)>w.charAt(i)) return false;
86           }
87           return v.length()<w.length();//如果指定位置的字符都相等,则比较长度
88       }
89      public static void main(String[] args) {
90         String[] a= {"159","126","654","354","681"};
91         sort(a);
92         for(String s:a)
93         {
94             System.out.println(s);
95         }
96     }
97
98 }

算法分析:

小数组(字符串)问题:

当我们需要排序的字符串有数百万个(ASCII集),但是大部分字符串的长度都在10左右,如果我们不对这些小型数组进行处理,那么每次排序都需要初始化258个count[]里的元素并将它们都转化为索引,这部分需要的代价很高,因此对小型的字符串进行插入排序是很有必要的。

等值键:

对于含有大量等值键的子数组排序会非常慢,最坏二点情况就是所有的键都相同。在实际问题中,我们可能会遇到大量含有相同前缀的字符串。(解决的方法是可以使用三向字符串快速排序)

额外空间:

为了切分,我们使用了两个辅助数组,aux[]和count[]。

原文地址:https://www.cnblogs.com/lls101/p/11277735.html

时间: 2024-11-05 18:31:24

String之字符串排序的相关文章

字符串排序和多字段排序(string sorting and multi-fields)

被分词的string字段也是一个multi-value字段,但是对他们进行排序往往得不到想要的结果.如果你对"fine old art"进行分词,他将会返回三个term.我们也许对以一个term进行字母排序,然后第二个等.但是ES没有在这个期间的时间顺序. 你可以使用min和max排序模式(默认使用min),但是结果既不是art或者old也不是任何想要的结果. 为了对string field进行排序,这个field就只能是一个term:也就是一个not_analyzed类型的strin

Openjudge-计算概论(A)-字符串排序

描述 参考整数排序方法,设计一种为字符串排序的算法,将字符串从小到大输出 输入 第一行为测试数据组数t, 后面跟着t组数据.每组数据第一行是n,表示这组数据有n行字符串,接下来是要排序的n行字符串.每行字符串的字符个数不会大于200, n < 100. 输出 对于每组数据,输出排好序的字符串,每组输出后要多输出一个空行 样例输入 2 2 Hello World 4 I Love C Language! 样例输出 Hello World C I Language! Love思路:这题可以把它们全部

C# 数据类型之 String(字符串)

?  前言 在开发中最常见的数据类型就是 String 类型,即字符串类型.为什么要单独讨论下这个类型,是因为:它是系统内置的基础数据类型:它的使用频率非常高:它是一个特殊的引用类型.其实大家都会使用它,但可能或多或少了解不够全面,本文主要是抱着:学习.巩固.总结的目的去加深对它的了解,主要学习一下几点: 1.   什么是 string 类型 2.   创建 string 对象的方式 3.   String 的常用静态方法 4.   string 的常用实例方法 5.   string 的常用扩

gets()、puts()函数。字符串函数。字符串排序的例子。

1.实例程序:string.c的程序: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include<stdio.h> #define MSG "YOU MUST have many talents .tell me some." #define LIM 5 #define LINELEN 81 int main() { char name[LINELEN]; char

leetCode 179. Largest Number 字符串排序 | Medium

179. Largest Number Given a list of non negative integers, arrange them such that they form the largest number. For example, given [3, 30, 34, 5, 9], the largest formed number is 9534330. Note: The result may be very large, so you need to return a st

关于字符串排序合并的问题

今天遇到了一个问题,题目大意是输入两个字符串,然后给这两个字符串按照ASCII码从小到大进行排序,最后在将两个字符串合并,要求删除其中相同的字符.一开始的时候感觉挺简单的一道题,但是做起来还是小毛病挺多的.还是直接看代码吧,代码里面的注释有许多需要注意的地方. 1 #include<stdio.h> 2 #include<string.h> 3 void sort(char *p) //给字符串排序,参数为字符串首地址 4 { 5 char temp; 6 char *head,*

九度OJ 1066 字符串排序

题目1066:字符串排序 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:4919 解决:1983 题目描述: 输入一个长度不超过20的字符串,对所输入的字符串,按照ASCII码的大小从小到大进行排序,请输出排序后的结果 输入: 一个字符串,其长度n<=20 输出: 输入样例可能有多组,对于每组测试样例, 按照ASCII码的大小对输入的字符串从小到大进行排序,输出排序后的结果 样例输入: dcba 样例输出: abcd #include<stdio.h> #include<

51 nod 1097 拼成最小的数 思路:字符串排序

题目: 思路:1.以字符串输入这些整数. 2.对这些字符串排序,排序规则为尽量让能让结果变小的靠前. 代码中有注释,不懂的欢迎在博客中评论问我. 代码: #include <bits\stdc++.h> using namespace std; string a[10001]; //比较规则:尽量让结果字符串最小 bool cmp(string a,string b){ return a+b <= b+a; } int main(){ int n; cin >> n; for

C语言之函数调用18—多字符串排序

//字符串排序***** /* =============================================================== 对任意个字符串升序排列! =============================================================== */ #include<stdio.h> #include<string.h> #define N 3          //控制排序数量 #define M 100