十九、字符串排序算法

字母表类

一些应用程序可能对字符串的字母表作出限制。在这些应用中,可能常常需要会需要一个API来表示Alphabet类(只是参考,并不会使用该类讨论算法)

public class Alphabet {

    /**
     *  The binary alphabet { 0, 1 }.
     */
    public static final Alphabet BINARY = new Alphabet("01");

    /**
     *  The octal alphabet { 0, 1, 2, 3, 4, 5, 6, 7 }.
     */
    public static final Alphabet OCTAL = new Alphabet("01234567");

    /**
     *  The decimal alphabet { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }.
     */
    public static final Alphabet DECIMAL = new Alphabet("0123456789");

    /**
     *  The hexadecimal alphabet { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F }.
     */
    public static final Alphabet HEXADECIMAL = new Alphabet("0123456789ABCDEF");

    /**
     *  The DNA alphabet { A, C, T, G }.
     */
    public static final Alphabet DNA = new Alphabet("ACTG");

    /**
     *  The lowercase alphabet { a, b, c, ..., z }.
     */
    public static final Alphabet LOWERCASE = new Alphabet("abcdefghijklmnopqrstuvwxyz");

    /**
     *  The uppercase alphabet { A, B, C, ..., Z }.
     */

    public static final Alphabet UPPERCASE = new Alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ");

    /**
     *  The protein alphabet { A, C, D, E, F, G, H, I, K, L, M, N, P, Q, R, S, T, V, W, Y }.
     */
    public static final Alphabet PROTEIN = new Alphabet("ACDEFGHIKLMNPQRSTVWY");

    /**
     *  The base-64 alphabet (64 characters).
     */
    public static final Alphabet BASE64 = new Alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");

    /**
     *  The ASCII alphabet (0-127).
     */
    public static final Alphabet ASCII = new Alphabet(128);

    /**
     *  The extended ASCII alphabet (0-255).
     */
    public static final Alphabet EXTENDED_ASCII = new Alphabet(256);

    public static final Alphabet UNICODE16      = new Alphabet(65536);
    private char[] alphabet;     // the characters in the alphabet
    private int[] inverse;       // indices
    private int R;               // the radix of the alphabet
    public Alphabet(String alpha) {

        // check that alphabet contains no duplicate chars
        boolean[] unicode = new boolean[Character.MAX_VALUE];
        for (int i = 0; i < alpha.length(); i++) {
            char c = alpha.charAt(i);
            if (unicode[c])
                throw new IllegalArgumentException("Illegal alphabet: repeated character = ‘" + c + "‘");
            unicode[c] = true;
        }

        alphabet = alpha.toCharArray();
        R = alpha.length();
        inverse = new int[Character.MAX_VALUE];
        for (int i = 0; i < inverse.length; i++)
            inverse[i] = -1;

        // can‘t use char since R can be as big as 65,536
        for (int c = 0; c < R; c++)
            inverse[alphabet[c]] = c;
    }
    private Alphabet(int R) {
        alphabet = new char[R];
        inverse = new int[R];
        this.R = R;

        // can‘t use char since R can be as big as 65,536
        for (int i = 0; i < R; i++)
            alphabet[i] = (char) i;
        for (int i = 0; i < R; i++)
            inverse[i] = i;
    }
    public Alphabet() {
        this(256);
    }
    public boolean contains(char c) {
        return inverse[c] != -1;
    }
    public int R() {
        return R;
    }
    public int lgR() {
        int lgR = 0;
        for (int t = R-1; t >= 1; t /= 2)
            lgR++;
        return lgR;
    }
    public int toIndex(char c) {
        if (c >= inverse.length || inverse[c] == -1) {
            throw new IllegalArgumentException("Character " + c + " not in alphabet");
        }
        return inverse[c];
    }
    public int[] toIndices(String s) {
        char[] source = s.toCharArray();
        int[] target  = new int[s.length()];
        for (int i = 0; i < source.length; i++)
            target[i] = toIndex(source[i]);
        return target;
    }
    public char toChar(int index) {
        if (index < 0 || index >= R) {
            throw new IndexOutOfBoundsException("Alphabet index out of bounds");
        }
        return alphabet[index];
    }
    public String toChars(int[] indices) {
        StringBuilder s = new StringBuilder(indices.length);
        for (int i = 0; i < indices.length; i++)
            s.append(toChar(indices[i]));
        return s.toString();
    }
}

字符串排序

索引计数法

输入字符串和字符串对应的组别(组别也是字符串的),在满足组别有小到大排序的情况下,将字符串按字母顺序排序

1. 记录组别的频率(为了得到某个字符串在排序后的范围,比如它肯定小于比它组别大的字符串,大于比它组别小的字符串)

cout记录频率,记录的位置是键值+1,加1是方便后期更新键的位置起点。

2. 转化为索引(得到每个组别的位置起点)

3. 排序、分类

先排序(本例输入是排好的序),排好了再分类(分类是该类有一个元素位置放好了,就把该类别的下限+1,给后面该类的元素放)

索引计数法是稳定的(就排序的稳定性来说)

低位优先排序

结合索引排序,从字符串的低位(从右面开始),从右到左,每个字符都当一次该字符串的键,给整个字符串排序,每一次往高位排,字符串的顺序可能跟之前的不一样了,但整个过程走完,字符串是有序的。(还是稳定性的性质是关键,什么是稳定,就是假如出现了高位时相同的,字符串的顺序肯定也是按次高位排的)

public class LSD {
    private static final int BITS_PER_BYTE = 8;
    // do not instantiate
    private LSD() { }
    public static void sort(String[] a, int W) {
        int N = a.length;
        int R = 256;   // extend ASCII alphabet size
        String[] aux = new String[N];

        for (int d = W-1; d >= 0; d--) {
            // sort by key-indexed counting on dth character

            // compute frequency counts
            int[] count = new int[R+1];
            for (int i = 0; i < N; i++)
                count[a[i].charAt(d) + 1]++;

            // compute cumulates
            for (int r = 0; r < R; r++)
                count[r+1] += count[r];

            // move data
            for (int i = 0; i < N; i++)
                aux[count[a[i].charAt(d)]++] = a[i];

            // copy back
            for (int i = 0; i < N; i++)
                a[i] = aux[i];
        }
    }
    public static void sort(int[] a) {
        int BITS = 32;                 // each int is 32 bits
        int W = BITS / BITS_PER_BYTE;  // each int is 4 bytes
        int R = 1 << BITS_PER_BYTE;    // each bytes is between 0 and 255
        int MASK = R - 1;              // 0xFF

        int N = a.length;
        int[] aux = new int[N];

        for (int d = 0; d < W; d++) {         

            // compute frequency counts
            int[] count = new int[R+1];
            for (int i = 0; i < N; i++) {
                int c = (a[i] >> BITS_PER_BYTE*d) & MASK;
                count[c + 1]++;
            }

            // compute cumulates
            for (int r = 0; r < R; r++)
                count[r+1] += count[r];

            // for most significant byte, 0x80-0xFF comes before 0x00-0x7F
            if (d == W-1) {
                int shift1 = count[R] - count[R/2];
                int shift2 = count[R/2];
                for (int r = 0; r < R/2; r++)
                    count[r] += shift1;
                for (int r = R/2; r < R; r++)
                    count[r] -= shift2;
            }

            // move data
            for (int i = 0; i < N; i++) {
                int c = (a[i] >> BITS_PER_BYTE*d) & MASK;
                aux[count[c]++] = a[i];
            }

            // copy back
            for (int i = 0; i < N; i++)
                a[i] = aux[i];
        }
    }
    public static void main(String[] args) {
        String[] a = StdIn.readAllStrings();
        int N = a.length;

        // check that strings have fixed length
        int W = a[0].length();
        for (int i = 0; i < N; i++)
            assert a[i].length() == W : "Strings must have fixed length";

        // sort the strings
        sort(a, W);

        // print results
        for (int i = 0; i < N; i++)
            StdOut.println(a[i]);
    }
}

高位优先字符串排序

在字符串长度不一定相同的情况下,从左至右排序。和上面不同的是,这里排完一个高位,就忽略这个高位,然后对同样的高位再用次高位排序(根据高位切分字符串),一直到最右

时间: 2024-10-05 05:54:11

十九、字符串排序算法的相关文章

九大排序算法总结

九大排序算法再总结 算法的由来:9世纪波斯数学家提出的:“al-Khowarizmi” 排序的定义: 输入:n个数:a1,a2,a3,...,an 输出:n个数的排列:a1',a2',a3',...,an',使得a1'<=a2'<=a3'<=...<=an'. In-place sort(不占用额外内存或占用常数的内存):插入排序.选择排序.冒泡排序.堆排序.快速排序. Out-place sort:归并排序.计数排序.基数排序.桶排序. 当需要对大量数据进行排序时,In-plac

九大排序算法Java实现

之前学习数据结构与算法时花了三天时间整理九大排序算法,并采用Java语言来实现,今天第一次写博客,刚好可以把这些东西从总结的文档中拿出来与大家分享一下,同时作为自己以后的备忘录. 1.排序算法时间复杂度.稳定性分类: 2.排序算法问题描述与实现 2.1冒泡排序(交换排序-稳定) [问题描述]对于一个int数组,请编写一个冒泡排序算法,对数组元素排序. 问题分析:冒泡排序,顾名思义,从前往后遍历,每次遍历在末尾固定一个最大值. 易错点:每次内层循环结束都会在末尾确定一个元素的位置,因此内层循环的判

python 十大经典排序算法

python 十大经典排序算法 排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存.常见的内部排序算法有:插入排序.希尔排序.选择排序.冒泡排序.归并排序.快速排序.堆排序.基数排序等.用一张图概括: 关于时间复杂度: 平方阶 (O(n2)) 排序 各类简单排序:直接插入.直接选择和冒泡排序. 线性对数阶 (O(nlog2n)) 排序 快速排序.堆排序和归并排序. O(n1+§)) 排序,§

19. 蛤蟆的数据结构进阶十九外部排序相关概念

19. 蛤蟆的数据结构进阶十九外部排序相关概念 本篇名言:"一个人最怕不老实,青年人最可贵的是老实作风. "老实 " 就是不自欺欺人,做到不欺骗人家容易,不欺骗自己最难. "老实作风 " 就是脚踏实地,不占便宜.世界上没有便宜的事,谁想占便宜水就会吃亏. --徐特立" 之前我们学习的排序都是内部排序的,接下去来看下外部排序. 欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/47

九大排序算法及其实现- 插入.冒泡.选择.归并.快速.堆排序.计数.基数.桶排序

  闲着的时候看到一篇“九大排序算法在总结”,瞬间觉得之前数据结构其实都有学过,但当初大多数都只是老师随口带过,并没有仔细研究一下.遂觉:这是欠下的账,现在该还了.   排序按照空间分类: In-place sort不占用额外内存或占用常数的内存 插入排序.选择排序.冒泡排序.堆排序.快速排序. Out-place sort:归并排序.计数排序.基数排序.桶排序. 或者按照稳定性分类: stable sort:插入排序.冒泡排序.归并排序.计数排序.基数排序.桶排序. unstable sort

九大排序算法,你会几个?

概述排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. 当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序.堆排序或归并排序序. 快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短: 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 将一个记录插入到已

Javascript九大排序算法详解

排序很多时候都会用到,而在js中排序的算法有九个是人们常用的,而且使用起来可以很流畅.本文将对这九种排序算法进行详细介绍,教程尚硅谷JavaScript DOM视频教程还有详细的代码分享哦. 一.插入排序 1)算法简介 插 入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法.它的工作原理是 通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入.插入排序在实现上,通常采用in-place排序(即只需用到 O(1)的额外空间的排序),因而在从后向前扫

数据结构(十二)——排序算法

数据结构(十二)--排序算法 一.排序简介 1.排序的一般定义 排序是计算机中经常进行的操作,目的在于将一组无序的数据元素调整为有序的数据元素.序列:1,20,45,5,2,12排序后:1,2,5,12,20,45 2.排序的数学定义 3.排序的稳定性 如果序列中的两个元素R[i].R[j],关键字分别为K[i].K[j],并且在排序之前R[i]排在R[j]前面,如果排序操作后,元素R[i]仍然排在R[j]前面,则排序方法是稳定的:否则排序是不稳定的. 4.排序实现的关键 比较:任意两个数据元素

十大经典排序算法(python实现)(原创)

经典排序算法图解: 经典排序算法的复杂度: 大类一(比较排序法): 1.冒泡排序(Bubble Sort) python代码实现: 1 d0 = [2, 15, 5, 9, 7, 6, 4, 12, 5, 4, 2, 64, 5, 6, 4, 2, 3, 54, 45, 4, 44] 2 d0_out = [2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7, 9, 12, 15, 44, 45, 54, 64] # 正确排序 3 4 while 1: 5 stat

一文搞定十大经典排序算法(Java实现)

本文总结十大经典排序算法及变形,并提供Java实现. 参考文章: 十大经典排序算法总结(Java语言实现) 快速排序算法—左右指针法,挖坑法,前后指针法,递归和非递归 快速排序及优化(三路划分等) 一.排序算法概述 1.定义 将杂乱无章的数据元素,通过一定的方法按关键字顺序排列的过程叫做排序. 2.分类 十种常见排序算法可以分为两大类: 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序. 线性时间非比较类排序:不通过比较