Bucket sorting — сортировка не на основе сравнения [подробно]

Алгоритм сортировки

Сортировка, известная ранеесортировка выбором,Пузырьковая сортировка,Сортировка вставками,Сортировка слиянием,быстрая сортировкаВсе они сортируются на основе сравнения, а групповая сортировка предлагает новую идею, то есть сортировку на основе состояния данных.

Сортировка по идее ковшовой сортировки

  • сортировка по подсчету
  • сортировка по основанию

Особенности сортировки ведром

  • Сортировка по идее сортировки ведра не основана на сравнении.
  • Временная сложность — O(N), а дополнительная пространственная нагрузка — O(M).
  • Область применения ограничена, и состояние данных образца должно соответствовать сегменту ведра.

Требования к классической сортировке по счету и сортировке по основанию для проб

  • Вообще говоря, сортировка подсчетом требует, чтобы выборки были целыми числами, а диапазон был относительно узким.
  • Вообще говоря, сортировка по основанию требует, чтобы выборки были положительными целыми числами по основанию 10.

Идея сортировки ведром

  1. Получить диапазон значений неупорядоченного массива

image.png2. «Создать» соответствующее количество «сегментов» в соответствии с диапазоном значений

image.png3. Пройдитесь по массиву и поместите каждый элемент в соответствующее «ведро».

image.png

4. Пройдитесь по каждому элементу корзины по порядку и по очереди поместите их в массив, чтобы завершить сортировку массива.

«Корзина» — это контейнер, который может быть реализован с различными структурами данных, включая массивы, очереди или стеки.

сортировка по подсчету

диаграмма

1. Найдите максимальное значение неупорядоченного массива и создайте пустой массив с длиной максимального значения + 1

image.png2. Пройдите по исходному массиву и подсчитайте количество вхождений каждого элемента.

image.png3. Проходим по «ковшу», то есть по массиву, используемому для подсчета количества элементов, и получаем упорядоченный массив

image.png

код

public static void countSort(int[] arr){
        if (arr == null ||arr.length<2){
            return;
        }
        //找到数组中最大的数
        int max = Integer.MIN_VALUE;
        for (int i =0;i<arr.length;i++){
            max = Math.max(max,arr[i]);
        }
        //实例化一组桶
        int[] bucket =  new int[max+1];
        //把数组中的数放到自己的桶中
        for (int i=0;i<arr.length;i++){
            bucket[arr[i]]++;
        }
        //按照桶的顺序把数倒出来
        int i=0;
        for (int j=0;j<bucket.length;j++){
            while (bucket[j]-->0){
                arr[i++]=j;
            }
        }
    }

классическая сортировка по основанию

Принцип состоит в том, чтобы разрезать целое число на разные числа по цифрам, а затем сравнивать каждую цифру отдельно. Изобретение сортировки по основанию можно проследить до вклада Германа Холлери в машину табуляции в 1887 году.

Метод поразрядной сортировки использует сегменты Как следует из названия, при назначении сравниваемых битов (единицы, десять, сотни...) сортируемые элементы распределяются по сегментам 0~9, чтобы достичь эффекта сортировки. В некоторых случаях поразрядная сортировка более эффективна, чем другие сравнительные сортировки.

Шаг алгоритма

  1. Найдите наибольшее число в данных и определите самый большой бит в отсортированном массиве.
  2. Унифицируйте все сравниваемые значения (целые положительные числа) до одинаковой длины цифр, а перед числами с более короткими цифрами добавляются нули.
  3. Сортировать от младшего разряда по порядку
  4. После сортировки от низшего порядка к высшему последовательность становится упорядоченной последовательностью

анимация

jspx.gif

диаграмма

image.png

Пример следующего массива

 int[] arrays = {6,  4322, 432, 344, 55 };

Теперь у нас есть 10 ведер, каждое из которых может содержать числа array.length.

int[][] buckets = new int[arrays.length][10];

image.png

1. Разместите каждую [одну цифру] массива в другом сегменте:

image.png

После выделения перерабатываем по порядку: результат должен быть такой: {4322,432,344,55,6}

2. Разместите каждый [десятизначный] массив в другом сегменте (для чисел, подобных 6, добавьте 0 впереди):

image.png

После выделения перерабатываем по порядку: результат должен быть таким: {6,4322,432,344,55}

3. Разместите каждую [сотенную цифру] массива в другом сегменте (числа, такие как 6, 55, добавьте 0 впереди):

image.png

После выделения перерабатываем по порядку: результат должен быть таким: {6,55,4322,344,432}

4. Разместите каждую [тысячную цифру] массива в другом сегменте (числа, такие как 6, 55, добавьте 0 впереди)

image.png

После выделения перерабатываем по порядку: результат должен быть таким: {6,55,344,432,4322}

код

public static int findMax(int[] arrays) {

        int max = Integer.MIN_VALUE;
        for (int i = 0; i < arrays.length; i++) {
            max = Math.max(max, arrays[i]);
        }
        return max;
    }

    public static void radixSort(int[] arrays) {
        int max = findMax(arrays);
        //需要遍历的次数由数组最大值的位数来决定
        for (int i = 1; max / i > 0; i = i * 10) {
            int[][] buckets = new int[arrays.length][10];
            //获取每一位数字(个、十、百、千位...分配到桶子里)
            for (int j = 0; j < arrays.length; j++) {
                int num = (arrays[j] / i) % 10;
                //将其放入桶子里
                buckets[j][num] = arrays[j];
            }
            //回收桶子里的元素
            int k = 0;
            //有10个桶
            for (int j = 0; j < 10; j++) {
                //对每个桶子里的元素进行回收
                for (int l = 0; l < arrays.length ; l++) {
                    //如果桶子里面有元素就回收(数据初始化会为0)
                    if (buckets[l][j] != 0) {
                        arrays[k++] = buckets[l][j];
                    }
                }
            }

        }
    }

Обновленная сортировка по основанию

Классическая сортировка по основанию требует дополнительного стека или очереди, но для достижения того же эффекта можно использовать количество вхождений каждой цифры и порядок обхода.

диаграмма

image.png

код

public static void radixSortPlus(int[] arr){
        if (arr == null ||arr.length<2){
            return;
        }
        radixSortPlus(arr,0,arr.length-1,maxbits(arr));
    }

    //arr[l..r]排序  ,  digit 数组中最大的位数
    public static void radixSortPlus(int[] arr,int L,int R,int digit){
        final int radix = 10;
        int i=0;
        int j=0;
        //有多少个数就准备多少个辅助空间
        int[] help =  new int[R-L+1];
        for (int d=1;d<=digit;d++){ // 最大多少位,就进出多少次
            // 10个空间
            // count[0] 当前位(d位)是0的数字有多少个
            // count[1] 当前位(d位)是(0和1)的数字有多少个
            // count[2] 当前位(d位)是(0、1和2)的数字有多少个
            // count[i] 当前位(d位)是(0~i)的数字有多少个
            int[] count = new int[radix]; // count[0..9]
            for (i=L;i<=R;i++){
                j = getDigit(arr[i],d);//得到当前位置上的数
                count[j]++;
            }
            //把count 数组变成累加和的形式
            for (i=1;i<radix;i++){
                count[i] = count[i]+count[i-1];
            }
            //从右往左遍历
            for (i=R;i>=L;i--){
                j = getDigit(arr[i],d);
                help[count[j]-1] =arr[i];
                count[j]--;
            }
            for (i = L, j = 0; i <= R; i++, j++) {
                arr[i] = help[j];
            }
        }
    }

    public static int getDigit(int x,int d){
        //x /10 的几次方 然后和10 取模,得到该位置上的数
        return ((x/((int)Math.pow(10,d-1)))%10);
    }

пс: некоторые картинки из интернета, вторглись и удалили.

Сортировать сводку

стабильность

image.png

Краткое изложение алгоритмов сортировки

  • 1) Сортировка не основана на сравнении, предъявляет строгие требования к выборочным данным и ее нелегко переписать.
  • 2) Сортировка на основе сравнения, если указано, как сравнивать размер двух выборок, ее можно повторно использовать напрямую.
  • 3) Сортировка на основе сравнения, предел сложности по времени равен O(N*logN)
  • 4) Временная сложность O(N*logN), дополнительная пространственная сложность ниже O(N) и не существует стабильной сортировки на основе сравнения.
  • 5) Выберите быструю строку для абсолютной скорости, выберите строку кучи для экономии места и выберите слияние для стабильности.