字符串之排序

字符串html

Java中String类来表示字符串,主要特性:java

字符:String由一系列字符组成,字符的类型char,7位ascii,16位Unicode编码。ios

不可变性:String对象不可变。git

索引:String类中的charAt()。github

长度:String类中的length()。算法

子字符串:subString()。shell

字符串的链接:StringBuilder,toString,+运算符。数组

字符串的表示:字符数组和Java字符串。char[] a,new String(a);String s,s.toCharArray()。maven

 

字符串排序ide

利用字符串的特殊性质将字符串键排序的方法。

第一类方法:从右至左检查键中的字符,低位优先Least-Significant-Digit-First,LSD,使用数字代替字符,若是将一个字符串看作一个256进制的数字,那么从右向左检查字符串就等于先检查低位的最低位。这种方法最适合用于键的长度都相同的字符串排序应用。

第二类方法:从左至右检查键中的字符,高位优先,MSD,吸引人之处在于不必定须要检查全部的输入就可以完成排序。

 

键索引计数法:将N个键为0至R-1之间的整数的元素须要访问数组11N+4R+1次。

初始化数组会访问数组N+R+1,在第一次循环中,N个元素都会让计数器加1,访问数组2N次,第二次循环会进行R次假发,访问数组2R次,第三次循环会使计数器的值增大N次并移动N次,访问数组2N次。全部的移动操做,都维护了等键元素的相对顺序。

int N = a.length;

String[] aux = new String(N);

int[] count = new int(R+1)

//计算频率

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

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

//将频率转化为索引

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

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

//将元素分类

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

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

//回写

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

         a[i]= aux[i];

 

低位优先的字符串排序:可以稳定地将定长字符串排序。

若是字符串的长度都为W,那就从右向左以每一个位置的字符做为键,用键索引计数法将字符串排序W遍。倒序保证排序的稳定性。

public class LSD {

         publicstatic void sort(String[] a,int w) {

                  //经过前W个字符将a[]排序

                  intN = a.length();

                  intR = 256; //字符串ascii7位。以char的int值来做为编号顺序

                  Stirng[]aux = new String[N];

                  for(intd = W-1;d>=0;d--) {

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

                          //计算出现频率

                          for(inti = 0;i<N;i++) {

                                   count[a[i].charAt(w)+1]++;

                          }

                          //将频率转换为索引

                          for(intr = 0;r<R;r++) {

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

                          }

                          //将元素分类

                          for(inti = 0;i<N;i+=) {

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

                          }

                          //将元素还原

                          for(inti = 0;i<N;i++) {

                                   a[i]= aux[i];

                          }

                  }

 

         }

}

输入      排序结果

43ABCE   23AADF

23ABDF   23ABDF

23AADF   43ABCE

 

对于基于R个字符的子母表的N个以长为W的字符串为键的元素,低位优先的字符串排序须要访问~7WN+3WR次数组,使用的额外空间与N+R成正比。

 

高位优先的字符串排序

 

对于长度不一的字符串,咱们能够通常从左到右的进行排序。首先用键索引计数法按照首字母排序,而后递归地再将每一个首字母对应的字数组排序,忽略首字母,由于首字母都是同样的,它将数组切分为可以独立排序的字数组来完成任务,但它的切分会为每一个首字母获得一个字数组。

输入           输出

she           are

shells          by

seashells        seashells

by            seashells

the            seashore

seashore       shells

the           shells

shells          she

she            she

sells           shells

are            surely

surely          the

seashells        the

 

对子字符串末尾的约定

将全部字符都已经被检查过的字符串所在的字数组排在全部子数组的前面,这样就不须要递归地将这个字数组排序。

将字符串中的字符索引转化为数组索引,当指定的位置超过了字符串的末尾时,该方法返回-1,而后全部的返回值加1,获得一个非负的int值,并用它做为count[]索引。这种转化意味字符串中的每一个字符均可能产生R+1种不一样的值:0表示字符串的结尾,i表示第i个字符。由于键索引计数法自己须要一个多余的位置,因此int count[] = new int[R+2],建立记录频率的数组,count[]数组中的值在统计频率、转换为索引并将数据分类后,它正是每一个字符所对应的字数组排序时所须要的位置。

本身的理解:

字符串数值 String[] a 

字符串长度N = a.length

置换字符串数值String[] aux

键值索引int count[R=256+2]

count[0]=0的存在便于count[i]=count[i]+count[i-1]计算频率的统一性

count[1]用来存放长度不够的字符串个数。好比计算[ab][a] [ad]计算到第二个字符时,[a]没有对应的,这样的应该存放到前面,则将其存放在第一个count[1]中,获得优先排。

 

input

a

ab

ab

dcf

acd

 

sort(a,lo,hi,d)

d从第一个(d=0)慢慢递增到字符串最长来排序。

第一次排序后,获得通过键值排序的新的String[] a,这里按照首字母a,b,c拍下去,若是这样的首字符存在的话。

a

ab

ab

acd

dcf

 

接着递归地调用sort

简化地将键值从0-R所有都递归,若是那些字数组字符串中的首字母不在的话,也递归进去,不在则在终止递归中进行操做。

第d个

*a

*b

*c

*d

 

第一步:频率

第二步:索引

第三步:分类

第四步:回写


http://grepcode.com/file/repo1.maven.org/maven2/com.googlecode.princeton-java-algorithms/algorithms/4.0.1/edu/princeton/cs/algorithms/MSD.java



http://lixinzhang.github.io/string-sorts-zi-fu-chuan-pai-xu.html

对字符串的排序,主要涉及一种count技术,即根据字符的有限性经过计数的方式来划分rerank的位置。

LSD string sort

原理

  1. LSD算法是典型的针对固定长度字符串排序的方法,如对IP地址,帐号的排序。
  2. 主要的的思想,从右向左,对每一个位置的字符进行rerank。
  3. rerank, 采用一个counter记录每一个字符所对应字符串rerank后的起始位置。这个方法有小技巧和逻辑性,值得一读。Pay attention!

特性

  1. LSD是稳定的排序算法
  2. LSD算法须要使用7WN+3WR的数组访问以及N+R的内存空间。W为key string的长度,N为待排序元素个数,因为实际应用中R远远小于N,所以近似时间复杂度为WN。

Source Code

void LSD_sort(string arr[], int N, int W) {
    const int R = 256;
    string * aux = new string [N];
    for(int i=W-1; i>=0; i--){
        int counter[R] = {0};
        for(int j=0; j<N; j++){
            counter[arr[j][i]+1] += 1;
        }
        for(int j=1; j<R; j++){
            counter[j] += counter[j-1];
        }
        for(int j=0; j<N; j++){
            aux[counter[arr[j][i]]++] = arr[j];
        }
        for(int j=0; j<N; j++){
            arr[j] = aux[j];
        }
    }
    delete [] aux;
}

MSD string Sort

MSD is most-significant-digit-first.

原理

  1. MSD主要解决当字符串长度不一致时的,更广泛的的字符串排序问题
  2. 与LSD不一样,MSD方法从左向右进行比较,而且每次比较都会将原字符串序列分红多个partition。而后对每一个patition再进行子问题求解。
  3. MSD仍须要借助count技术,对不一样的字符串进行划分。但存在一个问题,由于利用count技术的时候,须要访问R数组访问,但与长度已经很小的子序列而言是不值得的,所以对于小长度的子问题,直接采用朴素的插入排序求解。

特性

  1. MSD主要针对不一样长度的字符串,所以当有字符串到达末尾的时候,即C++/C中的\0时,因为accsi码值为0,所以具备高优先级,使得其天然排序上升。
  2. MSD算法会耗费很是多的内存空间,由于在应用 count方法时,count数组不能放在外面做为类成员变量共享(辅助数组aux则能够)。
  3. MSD的算法效率与输入数据很是相关,如若第一列就能够很好的区分开256个部分,那么一次就完成了排序;最差的状况是,数组里的全部字符串都相等,那么就不得不一直走到队尾。
  4. MSD须要8N+3R ~ 7wN+3WR之间的数组访问次数。 其中w为平均的字符串长度。

https://sdfpaw.dm2301.livefilestore.com/y2pg0ab5n0cQhaMrbyvGdwW4u3dvM2ZKSluHbZhhK9cWcPwO9tJ_9ble_Bxsdx5l10g02TQQ5gkytn9_JVrvR2Sm_L8A24MbML6Mikdg1CUd-M/QQ20140627-1.png?psid=1

Source Code

#include<iostream>
#include<string>
using namespace std;

class MSD{
public:
    static void sort(string arr[], int size);
    static void sort(string arr[], int lo, int hi, int d);
    static void insertSort(string arr[], int lo, int hi, int d);
public:
    static string * aux;
    static int * a;
    const static int R;
    const static int M; //cutoff for small subarrays
};


const int MSD::R = 256;
const int MSD::M = 0;
string * MSD::aux = NULL;

void MSD::sort(string arr[], int size){
    if(size <= 1) return;
    MSD::aux = new string[size];
    sort(arr, 0, size-1, 0);
}
void MSD::sort(string arr[], int lo, int hi, int d){
    if(lo >= hi) return ;
    if(hi<lo+M){
        insertSort(arr, lo, hi, d);
        return ;
    }
    int count [R+1] = {0};
    for(int i=lo; i<=hi; i++){
        count[arr[i][d] + 1] ++;
    }
    for(int r=1; r<R+1; r++){
        count[r] += count[r-1];
    }
    for(int i=lo; i<=hi; i++){
        aux[count[arr[i][d]]++] = arr[i];
    }
    for(int i=lo; i<=hi; i++){
        arr[i] = aux[i-lo];
    }
    for(int r=0; r<R; r++){
        MSD::sort(arr, lo+count[r], lo+count[r+1]-1, d+1);
    }
}

void MSD::insertSort(string arr[], int lo, int hi, int d){
    for(int i=lo+1; i<=hi; i++){
        for(int j=i; j>lo; j--){
            if(arr[i][d] > arr[j][d]){
                swap(arr[i], arr[j]);
            }
        }
    }
}

int main(){
    //MSD::R = 10;
    string arr[] = {"she", "sells", "seashells", "by", "the", "sea",
    "shore", "the", "shells", "she", "sells", "are", "surely", "seashells"};
    int len = 14;
    MSD::sort(arr, len);
    for(int i=0; i<len; i++)
        cout<<arr[i]<<endl;
    return 0;
}

Three-way string quicksort

原理

  1. 参考MSD方法的思想,很容易想到一种基于partition的排序方法——quick sort.
  2. 咱们能够按照每一个位置的字符做为比较元素,而后划分为3个partition。根据以前所了解的三路快排,咱们知道中间的那份partition包含的元素是相同的,所以在进行子问题递归的时候,比较元素的位置+1。而另外两个,则继续在该位置上进行子问题计算。

特性

  1. 时间赋值度约定于2NlnN

源代码

class Quick3string{
public:
    static void sort(string arr[], int size){
        sort(arr, 0, size-1, 0);
    }
    static void sort(string arr[], int lo, int hi, int d){
        if(lo>=hi) return;
        int lt = lo, gt = hi;
        int v = arr[lo][d];
        int i = lo+1;
        while(i<=gt){
            int t = arr[i][d];
            if(t<v) swap(arr[lt++], arr[i++]);
            else if (t>v) swap(arr[i], arr[gt--]);
            else i++;
        }
        sort(arr, lo, lt-1, d);
        if(v>=0) sort(arr, lt, gt, d+1);
        sort(arr, gt+1, hi, d);
    }
};

Conclution

https://sdfpaw.dm2301.livefilestore.com/y2pEB9tecW9w8PnfRrPnhN0dGvP_-ZJiSmDQORpXT2gyaHvXDGvNoOzAfTzP34tytKjPcx8ZjUsCx-joMoVKMBV1OtaY2pFRBQLIe38tLtTkr8/QQ20140627-2.png?psid=1