字符串排序算法

字符串排序算法:

  • 键索引计数法
  • 低位优先的字符串排序算法(Least-Significant-Digit-First, LSD)
  • 高位优先的字符串排序算法(MSD)
  • 三向字符串快速排序

键索引计数法

适用性:适用于小整数键的排序算法

稳定性:稳定的排序算法,排序后键相同的元素的相对位置没有变化

思路:计算数据每个键的起始位置

  • 统计键出现的频率
  • 将频率转换成键在数组中的起始位置

算法实现:

/**
 * 键索引计数法
 * */
public class KeyIndexCount {
    private static final int R = 100; // 数字范围 [0, R)

    public static void sort(int[] data) {
        int n = data.length;
        // 统计频率
        int[] count = new int[R + 1];
        for (int i = 0; i < n; i++) {
            count[data[i] + 1]++;
        }
        // 将频率转换成索引
        for (int r = 0; r < R; r++) {
            count[r + 1] += count[r];
        }
        // 数据分类
        int[] aux = new int[n];
        for (int i = 0; i < n; i++) {
            aux[count[data[i]]] = data[i];
            count[data[i]]++;
        }
        // 回写
        System.arraycopy(aux, 0, data, 0, n);
    }
}

测试:

class KeyIndexCountTest {
    @Test
    void sort() {
        Random random = new Random();
        int[] data = new int[100];
        for (int i = 0; i < 100; i++) {
            data[i] = random.nextInt(100);
        }
        KeyIndexCount.sort(data);
        Assertions.assertTrue(SortUtils.isSorted(data));
    }
}

低位优先的字符串排序算法

适用性:适用于定长的字符串排序

思路:从右往左对每个字符应用键索引计数法

算法实现:

/**
 * 低位优先的字符串排序算法
 * */
public class LSD {
    private static final int R = 256; // 字符集大小

    /**
     * @param w 按前 w 位排序
     * */
    public static void sort(String[] data, int w) {
        int n = data.length;
        String[] aux = new String[n];
        // 从右往左对每个字符应用键索引计数法
        for (int d = w - 1; d >= 0; d--) {
            // 统计频率
            int[] count = new int[R + 1];
            for (int i = 0; i < n; i++) {
                count[data[i].charAt(d) + 1]++;
            }
            // 将频率转换为索引
            for (int r = 0; r < R; r++) {
                count[r + 1] += count[r];
            }
            // 数据分类
            for (int i = 0; i < n; i++) {
                aux[count[data[i].charAt(d)]] = data[i];
                count[data[i].charAt(d)]++;
            }
            // 回写
            System.arraycopy(aux, 0, data, 0, n);
        }
    }
}

测试:

class LSDTest {
    @Test
    void sort() {
        String[] data = new String[]{"4PGC938", "2IYE230", "3CIO720", "1ICK750", "1OHV845", "4JZY524", "1ICK750", "3CIO720", "1OHV845", "1OHV845", "2RLA629", "2RLA629", "3ATW723"};
        LSD.sort(data, 7);
        Assertions.assertEquals(
            "[1ICK750, 1ICK750, 1OHV845, 1OHV845, 1OHV845, 2IYE230, 2RLA629, 2RLA629, 3ATW723, 3CIO720, 3CIO720, 4JZY524, 4PGC938]",
            Arrays.toString(data)
        );
    }
}

高位优先的字符串排序算法

适用性:适用于变长字符串

思路:将字符串从左到右按字符排序

  • 将当前字符应用键索引计数法排序
  • 将数组按当前字符分组,递归处理下一个字符

算法实现:

/**
 * 高位优先字符串排序算法
 * */
public class MSD {
    private static final int R = 256; // 字符集大小
    private static String[] aux;

    public static void sort(String[] data) {
        aux = new String[data.length];
        sort(data, 0, data.length - 1, 0);
    }

    /**
     * @param lo 子数组开始位置
     * @param hi 子数组结束位置
     * @param d 处理索引位置为 d 的字符
     * */
    private static void sort(String[] data, int lo, int hi, int d) {
        if (lo >= hi) {
            return;
        }
        int n = data.length;
        // 统计频率
        int[] count = new int[R + 2];
        for (int i = lo; i <= hi; i++) {
            count[charAt(data[i], d) + 2]++;
        }
        // 将频率转换为索引
        for (int r = 0; r < R + 1; r++) {
            count[r + 1] += count[r];
        }
        // 数据分类
        for (int i = lo; i <= hi; i++) {
            aux[count[charAt(data[i], d) + 1]] = data[i];
            count[charAt(data[i], d) + 1]++;
        }
        // 回写
        for (int i = lo; i <= hi; i++) {
            data[i] = aux[i - lo];
        }
        // 递归处理子数组
        for (int r = 0; r < R; r++) {
            sort(data, lo + count[r], lo + count[r + 1] - 1, d + 1);
        }
    }

    /**
     * @return 字符串中索引位置为 d 的字符,超过字符串长度返回 -1
     * */
    private static int charAt(String s, int d) {
        if (d < s.length()) {
            return s.charAt(d);
        }
        return -1;
    }
}

测试:

class MSDTest {
    @Test
    void sort() {
        String[] data = new String[]{"she", "sells", "seashells", "by", "the", "seashore", "the", "shells", "she", "sells", "are", "surely", "seashells"};
        MSD.sort(data);
        Assertions.assertEquals(
            "[are, by, seashells, seashells, seashore, sells, sells, she, she, shells, surely, the, the]",
            Arrays.toString(data)
        );
    }
}

三向字符串快速排序

算法实现:

/**
 * 三向字符串快速排序
 * */
public class Quick3String {
    public static void sort(String[] data) {
        sort(data, 0, data.length - 1, 0);
    }

    private static void sort(String[] data, int lo, int hi, int d) {
        if (lo >= hi) {
            return;
        }
        int lt = lo;
        int gt = hi;
        int v = chatAt(data[lo], d);
        int i = lo + 1;
        while (i <= gt) {
            int t = chatAt(data[i], d);
            if (t < v) {
                exch(data, lt++, i++);
            } else if (t > v) {
                exch(data, gt--, i);
            } else {
                i++;
            }
        }
        sort(data, lo, lt - 1, d);
        if (v > 0) sort(data, lt, gt, d + 1);
        sort(data, gt + 1, hi, d);
    }

    private static int chatAt(String s, int d) {
        if (d < s.length()) {
            return s.charAt(d);
        }
        return -1;
    }

    private static void exch(String[] data, int i, int j) {
        String t = data[i];
        data[i] = data[j];
        data[j] = t;
    }
}

测试:

class Quick3StringTest {
    @Test
    void sort() {
        String[] data = new String[]{"edu.princeton.cs", "com.apple", "edu.princeton.cs", "com.cnn", "com.google", "edu.uva.cs", "edu.princeton.cs", "edu.princeton.cs.www", "edu.uva.cs", "edu.uva.cs", "edu.uva.cs", "com.adobe", "edu.princeton.ee"};
        Quick3String.sort(data);
        Assertions.assertEquals(
            "[com.adobe, com.apple, com.cnn, com.google, edu.princeton.cs, edu.princeton.cs, edu.princeton.cs, edu.princeton.cs.www, edu.princeton.ee, edu.uva.cs, edu.uva.cs, edu.uva.cs, edu.uva.cs]",
            Arrays.toString(data)
        );
    }
}
posted @ 2022-08-13 13:47  廖子博  阅读(97)  评论(0编辑  收藏  举报