21、计数排序

内容来自刘宇波老师算法与数据结构体系课

1、颜色分类

75 - 颜色分类

image

/**
 * 整数 0、1 和 2 分别表示红色、白色和蓝色
 * 对它们进行排序, 使得相同颜色的元素相邻, 并按照红色、白色、蓝色顺序排列
 */
@SuppressWarnings("all")
public class SortColors {

    /**
     * 计数排序
     */
    public static void sortColors(int[] nums) {
        int[] cnt = new int[3];
        for (int num : nums) cnt[num]++;

        // 0 [0, cnt0)
        // 1 [cnt0, cnt0 + cnt1)
        // 2 [cnt0 + cnt1, cnt0 + cnt1 + cnt2)
        for (int i = 0; i < cnt[0]; i++) nums[i] = 0;
        for (int i = cnt[0]; i < cnt[0] + cnt[1]; i++) nums[i] = 1;
        for (int i = cnt[0] + cnt[1]; i < cnt[0] + cnt[1] + cnt[2]; i++) nums[i] = 2;
    }
}

2、更一般的计数排序

image
image
image

/**
 * 整数 0、1 和 2 分别表示红色、白色和蓝色
 * 原地对它们进行排序, 使得相同颜色的元素相邻, 并按照红色、白色、蓝色顺序排列
 */
@SuppressWarnings("all")
public class SortColors {

    /**
     * 计数排序
     */
    public static void sortColors(int[] nums) {
        // nums 中的元素固定为 0, 1, 2
        // 处理元素取值范围是 [0, R) 的计数排序
        int R = 3;
        int[] cnt = new int[R];
        for (int num : nums) cnt[num]++;

        // [index[i], index[i + 1]) 区间的值为 i
        int[] index = new int[R + 1];
        for (int i = 0; i < R; i++) {
            // 右边界 = 左边界 + cnt[i]
            index[i + 1] = index[i] + cnt[i];
        }

        for (int i = 0; i + 1 < index.length; i++) {
            // [index[i], index[i + 1]) 区间的值为 i
            for (int j = index[i]; j < index[i + 1]; j++) nums[j] = i;
        }
    }
}

3、计数排序的稳定性

image
image

4、实现计数排序

/**
 * 计数排序是稳定的 O(N + R)
 */
public class CountingSort {

    private CountingSort() {
    }

    /**
     * 计数排序 O(N + R)
     */
    public static void sort(Student[] students, int R) {
        // 分数的取值范围是 [0, R)
        int[] cnt = new int[R];
        for (Student student : students) cnt[student.getScore()]++;

        // [index[i], index[i + 1]) 区间的值为 i
        int[] index = new int[R + 1];
        for (int i = 0; i < R; i++) index[i + 1] = index[i] + cnt[i];

        Student[] temp = new Student[students.length];
        for (Student student : students) {
            int score = student.getScore();
            temp[index[score]] = student;
            index[score] += 1;
        }

        System.arraycopy(temp, 0, students, 0, temp.length);
    }
}

5、验证计数排序的稳定性

public class Student implements Comparable<Student> {

    private String name;
    private int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || this.getClass() != o.getClass()) return false;
        Student another = (Student) o;
        return Objects.equals(name.toLowerCase(), another.name.toLowerCase());
    }

    @Override
    public int compareTo(Student another) {
        return this.score - another.score;
    }

    @Override
    public String toString() {
        return String.format("Student(name = %s, score = %d)", name, score);
    }
}
public class CountingSortTest {

    /**
     * 验证排序结果是否正确
     */
    private static void isSorted(Student[] arr) {
        int n = arr.length;

        for (int i = 1; i < n; i++) {
            if (arr[i - 1].getScore() > arr[i].getScore()) {
                throw new RuntimeException("Sort failed!");
            }

            if (arr[i - 1].getScore() == arr[i].getScore()) {
                if (arr[i - 1].getName().compareTo(arr[i].getName()) > 0) {
                    throw new RuntimeException("Non-Stable counting sort!");
                }
            }
        }
    }

    /**
     * 分数 [0, 101),  R = 101
     */
    public static void test() {
        int n = 26 * 26 * 26 * 26;
        Student[] students = new Student[n];
        Random random = new Random();

        int i = 0;
        for (char c1 = 'a'; c1 <= 'z'; c1++)
            for (char c2 = 'a'; c2 <= 'z'; c2++)
                for (char c3 = 'a'; c3 <= 'z'; c3++)
                    for (char c4 = 'a'; c4 <= 'z'; c4++)
                        students[i++] = new Student("" + c1 + c2 + c3 + c4, random.nextInt(101));

        CountingSort.sort(students, 101);
        isSorted(students);
    }

    public static void main(String[] args) {
        test();
    }
}
posted @ 2023-04-14 15:26  lidongdongdong~  阅读(9)  评论(0编辑  收藏  举报