常用排序算法

1.冒泡排序

冒泡排序在比较相邻元素时,只有在它们的大小关系不符合排序顺序时才会进行交换,而对于相等的元素,它们之间的相对位置不会改变,所以冒泡排序是稳定的。

代码:

// 冒泡排序算法
public static void bubbleSort(int[] arr) {
    int n = arr.length;
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                // 交换元素位置
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

引入一个标志位来表示是否发生了交换。如果在一轮遍历中没有发生交换,就可以提前结束排序,因为数组已经是有序的了。这样可以减少不必要的比较,提高性能。

// 优化的冒泡排序算法
public static void bubbleSort(int[] arr) {
    int n = arr.length;
    boolean swapped;
    for (int i = 0; i < n - 1; i++) {
        swapped = false;
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                // 交换元素位置
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swapped = true;
            }
        }
        // 如果没有发生交换,说明数组已经有序,提前退出循环
        if (!swapped) {
            break;
        }
    }
}

 

2.选择排序

选择排序(Selection Sort)是一种简单直观的排序算法,它的基本思想是每次从待排序的数据中选择最小(或最大)的元素,放到已排序序列的末尾(或开头),直到所有元素都被排序完毕。

它是不稳定的算法。

public static void selectionSort(int[] a) {
    // 1. 选择轮数 a.length - 1
    // 2. 交换的索引位置(right) 初始 a.length - 1, 每次递减
    for (int right = a.length - 1; right > 0 ; right--) {
        int max = right;
        for (int i = 0; i < right; i++) {
            if (a[i] > a[max]) {
                max = i;
            }
        }
        if(max != right) {
            int t = a[max];
            a[max] = a[right];
            a[right] = t;
        }
    }
}

 

3.堆排序

堆排序(Heap Sort)是一种基于堆数据结构的排序算法,它利用了堆的性质来进行排序。堆排序的基本思想是先将待排序的元素构建成一个最大堆(或最小堆),然后依次将堆顶元素(最大元素或最小元素)与堆的最后一个元素交换,并重新调整堆,使得剩余元素重新构成一个堆,然后重复这个过程,直到所有元素都被排序完毕。

它是不稳定的算法

package com.dreams.sort;

import java.util.Arrays;

/**
 * 堆排序
 */
public class HeapSort {
    public static void sort(int[] a) {
        heapify(a, a.length);
        for (int right = a.length - 1; right > 0; right--) {
            swap(a, 0, right);
            down(a, 0, right);
        }
    }

    // 建堆 O(n)
    private static void heapify(int[] array, int size) {
        for (int i = size / 2 - 1; i >= 0; i--) {
            down(array, i, size);
        }
    }

    // 下潜
    // leetcode 上数组排序题目用堆排序求解,非递归实现比递归实现大约快 6ms
    private static void down(int[] array, int parent, int size) {
        while (true) {
            int left = parent * 2 + 1;
            int right = left + 1;
            int max = parent;
            if (left < size && array[left] > array[max]) {
                max = left;
            }
            if (right < size && array[right] > array[max]) {
                max = right;
            }
            if (max == parent) { // 没找到更大的孩子
                break;
            }
            swap(array, max, parent);
            parent = max;
        }
    }

    // 交换
    private static void swap(int[] a, int i, int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

    public static void main(String[] args) {
        int[] a = {2, 3, 1, 7, 6, 4, 5};
        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println(Arrays.toString(a));
    }
}

 

 

4.插入排序

插入排序是一种简单直观的排序算法,它的工作原理是通过构建有序序列,对未排序的数据逐个进行插入,从而达到排序的目的。

代码:

package com.dreams.sort;

import java.util.Arrays;

/**
 * 插入排序
 */
public class InsertionSort {

    public static void sort(int[] a) {
        for (int low = 1; low < a.length; low++) {
            int t = a[low];
            int i = low - 1;
            // 自右向左找插入位置,如果比待插入元素大,则不断右移,空出插入位置
            while (i >= 0 && t < a[i]) {
                a[i + 1] = a[i];
                i--;
            }
            // 找到插入位置
            if (i != low - 1) {
                a[i + 1] = t;
            }
        }
    }

    public static void main(String[] args) {
        int[] a = {9, 3, 7, 2, 5, 8, 1, 4};
        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println(Arrays.toString(a));
    }
}

 

 

5.希尔排序

希尔排序(Shell Sort)是一种改进的插入排序算法,也称为缩小增量排序。希尔排序的基本思想是将原始序列分割成若干个子序列,对这些子序列分别进行插入排序,然后逐步缩小子序列的间隔,最终完成排序。

package com.dreams.sort;

import java.util.Arrays;

/**
 * 希尔排序
 */
public class ShellSort {
    public static void sort(int[] a) {
        for (int gap = a.length >> 1; gap >= 1; gap = gap >> 1) {
            // gap=4
            for (int low = gap; low < a.length; low++) {
                int t = a[low]; // t=5
                int i = low - gap;
                // 自右向左找插入位置,如果比待插入元素大,则不断右移,空出插入位置
                while (i >= 0 && t < a[i]) {
                    a[i + gap] = a[i];
                    i -= gap;
                }
                // 找到插入位置
                if (i != low - gap) {
                    a[i + gap] = t;
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] a = {9, 3, 7, 2, 5, 8, 1, 4};
        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println(Arrays.toString(a));
    }
}

 

6.归并排序

归并排序(Merge Sort)是一种基于分治思想的排序算法,它将待排序的序列递归地分成两个子序列,直到每个子序列只有一个元素为止,然后将相邻的子序列两两合并,同时将合并后的子序列排序,最终得到完全有序的序列。

稳定算法

自顶至下

代码:

package com.dreams.sort;

import java.util.Arrays;

/**
 * 归并排序 TopDown
 */
public class MergeSortTopDown {

    /*
        a1 原始数组
        i~iEnd 第一个有序范围
        j~jEnd 第二个有序范围
        a2 临时数组
     */
    public static void merge(int[] a1, int i, int iEnd, int j, int jEnd, int[] a2) {
        int k = i;
        while (i <= iEnd && j <= jEnd) {
            if (a1[i] < a1[j]) {
                a2[k] = a1[i];
                i++;
            } else {
                a2[k] = a1[j];
                j++;
            }
            k++;
        }
        if (i > iEnd) {
            System.arraycopy(a1, j, a2, k, jEnd - j + 1);
        }
        if (j > jEnd) {
            System.arraycopy(a1, i, a2, k, iEnd - i + 1);
        }
    }

    public static void sort(int[] a1) {
        int[] a2 = new int[a1.length];
        split(a1, 0, a1.length - 1, a2);
    }

    private static void split(int[] a1, int left, int right, int[] a2) {

        // 2. 治
        if (left == right) {
            return;
        }
        // 1. 分
        int m = (left + right) >>> 1;
        split(a1, left, m, a2);                 // left = 0 m = 0  9
        split(a1, m + 1, right, a2);       // m+1 = 1 right = 1  3
        // 3. 合
        merge(a1, left, m, m + 1, right, a2);
        System.arraycopy(a2, left, a1, left, right - left + 1);
    }

    public static void main(String[] args) {
        int[] a = {9, 3, 7, 2, 8, 5, 1, 4};
        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println(Arrays.toString(a));
    }
}

 

自下至上

就是非递归

代码:

package com.dreams.sort;

import java.util.Arrays;

/**
 * 归并排序 BottomUp
 */
public class MergeSortBottomUp {

    /*
        a1 原始数组
        i~iEnd 第一个有序范围
        j~jEnd 第二个有序范围
        a2 临时数组
     */
    public static void merge(int[] a1, int i, int iEnd, int j, int jEnd, int[] a2) {
        int k = i;
        while (i <= iEnd && j <= jEnd) {
            if (a1[i] < a1[j]) {
                a2[k] = a1[i];
                i++;
            } else {
                a2[k] = a1[j];
                j++;
            }
            k++;
        }
        if (i > iEnd) {
            System.arraycopy(a1, j, a2, k, jEnd - j + 1);
        }
        if (j > jEnd) {
            System.arraycopy(a1, i, a2, k, iEnd - i + 1);
        }
    }

    public static void sort(int[] a1) {
        int n = a1.length;
        int[] a2 = new int[n];
        // width 代表有序区间的宽度,取值依次为 1、2、4 ...
        for (int width = 1; width < n; width *= 2) {
            // [left, right] 分别代表待合并区间的左右边界
            for (int left = 0; left < n; left += 2 * width) {
                int right = Math.min(left + 2 * width - 1, n - 1);
//                System.out.printf("width %d [%d,%d]%n", width, left, right);
                int m = Math.min(left + width - 1, n - 1);
                merge(a1, left, m, m + 1, right, a2);
            }
            System.arraycopy(a2, 0, a1, 0, n);
        }
    }

    public static void main(String[] args) {
        int[] a = {9, 3, 7, 2, 8, 5, 1, 4};
        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println(Arrays.toString(a));
    }
}

 

归并加入插入排序

package com.dreams.sort;

import java.util.Arrays;

/**
 * 归并排序 + 插入排序
 */
public class MergeInsertionSort {
    public static void insertion(int[] a, int left, int right) {
        for (int low = left + 1; low <= right; low++) {
            int t = a[low];
            int i = low - 1;
            // 自右向左找插入位置,如果比待插入元素大,则不断右移,空出插入位置
            while (i >= left && t < a[i]) {
                a[i + 1] = a[i];
                i--;
            }
            // 找到插入位置
            if (i != low - 1) {
                a[i + 1] = t;
            }
        }
    }

    public static void merge(int[] a1, int i, int iEnd, int j, int jEnd, int[] a2) {
        int k = i;
        while (i <= iEnd && j <= jEnd) {
            if (a1[i] < a1[j]) {
                a2[k] = a1[i];
                i++;
            } else {
                a2[k] = a1[j];
                j++;
            }
            k++;
        }
        if (i > iEnd) {
            System.arraycopy(a1, j, a2, k, jEnd - j + 1);
        }
        if (j > jEnd) {
            System.arraycopy(a1, i, a2, k, iEnd - i + 1);
        }
    }

    public static void sort(int[] a1) {
        int[] a2 = new int[a1.length];
        split(a1, 0, a1.length - 1, a2);
    }

    private static void split(int[] a1, int left, int right, int[] a2) {
        // 2. 治
        if (right - left <= 32) {
            // 插入排序
            insertion(a1, left, right);
            return;
        }
        // 1. 分
        int m = (left + right) >>> 1;
        split(a1, left, m, a2);
        split(a1, m + 1, right, a2);
        // 3. 合
        merge(a1, left, m, m + 1, right, a2);
        System.arraycopy(a2, left, a1, left, right - left + 1);
    }

    public static void main(String[] args) {
        int[] a = {9, 3, 7, 2, 8, 5, 1, 4};
        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println(Arrays.toString(a));
    }
}

 

7.快速排序

快速排序是一种常用的排序算法,其基本思想是通过将数组分割成较小的子数组,然后递归地排序这些子数组。

步骤如下:

  1. 选择一个基准元素(pivot),通常选择数组的第一个元素。
  2. 将数组中小于基准元素的元素移到基准元素的左边,大于基准元素的元素移到基准元素的右边,基准元素则处于最终排好序的位置。
  3. 对基准元素左右两边的子数组分别递归地执行步骤 1 和步骤 2,直到各个子数组的大小为 1 或 0,表示已经排好序。

 

单边循环(lomuto分区)

  • 选择最右侧元素作为基准点
  • j 找比基准点小的,i 找比基准点大的,一旦找到,二者进行交换
  • 最后基准点与 i交换,i即为基准点最终索引
package com.dreams.sort;

import java.util.Arrays;

/**
 * 单边循环快排(lomuto 洛穆托分区方案)
 */
public class QuickSortLomuto {

    public static void sort(int[] a) {
        quick(a, 0, a.length - 1);
    }

    private static void quick(int[] a, int left, int right) {
        if (left >= right) {
            return;
        }
        int p = partition(a, left, right); // p代表基准点元素索引
        quick(a, left, p - 1);
        quick(a, p + 1, right);
    }

    private static int partition(int[] a, int left, int right) {
        int pv = a[right]; // 基准点元素值
        int i = left;
        int j = left;
        while (j < right) {
            if (a[j] < pv) { // j 找到比基准点小的了, 没找到大的
                if (i != j) {
                    swap(a, i, j);
                }
                i++;
            }
            j++;
        }
        swap(a, i, right);
        return i;
    }

    private static void swap(int[] a, int i, int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

    public static void main(String[] args) {
        int[] a = {5, 3, 7, 2, 9, 8, 1, 4};
        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println(Arrays.toString(a));
    }
}

 

双边循环

  • 选择最左侧元素作为基准点
  • j找比基准点小的,i 找比基准点大的,一旦找到,二者进行交换,i从左向右。j从右向左
  • 最后基准点与i交换,i即为基准点最终索引

代码:

要先处理 j,再处理 i,顺序不能变。
package com.dreams.sort;

import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 双边循环快排
 */
public class QuickSortHoare {

    public static void sort(int[] a) {
        quick(a, 0, a.length - 1);
    }

    private static void quick(int[] a, int left, int right) {
        if (left >= right) {
            return;
        }
        int p = partition(a, left, right);
        quick(a, left, p - 1);
        quick(a, p + 1, right);
    }

    /*
        注意事项
        1. 为啥要加内层循环的 i<j 条件
        2. 为啥要先处理 j,再处理 i
        3. 随机元素作为基准点
        4. 如果有大量重复元素
     */
    private static int partition(int[] a, int left, int right) {
        int idx = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
        // [0~9] right-left+1=3 [0,2]+4=[4,6]
        swap(a, idx, left);
        int pv = a[left];
        int i = left;
        int j = right;
        while (i < j) {
            // 1. j 从右向左找小(等)的
            while (i < j && a[j] > pv) {
                j--;
            }
            // 2. i 从左向右找大的
            while (i < j && a[i] <= pv) {
                i++;
            }
            // 3. 交换位置
            swap(a, i, j);
        }
        swap(a, left, i);
        return i;
    }

    private static void swap(int[] a, int i, int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

    public static void main(String[] args) {
        int[] a = {9, 3, 7, 2, 8, 5, 1, 4};
        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println(Arrays.toString(a));
    }
}

 

处理相等元素

代码:

package com.dreams.sort;

import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 双边循环快排 - 处理相等元素
 */
public class QuickSortHandleDuplicate {

    public static void sort(int[] a) {
        quick(a, 0, a.length - 1);
    }

    private static void quick(int[] a, int left, int right) {
        if (left >= right) {
            return;
        }
        int p = partition(a, left, right);
        quick(a, left, p - 1);
        quick(a, p + 1, right);
    }

    /*
        循环内
            i 从 left + 1 开始,从左向右找大的或相等的
            j 从 right 开始,从右向左找小的或相等的
            交换,i++ j--

        循环外 j 和 基准点交换,j 即为分区位置
     */
    private static int partition(int[] a, int left, int right) {
        int idx = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
        swap(a, left, idx);
        int pv = a[left];
        int i = left + 1;
        int j = right;
        while (i <= j) {
            // i 从左向右找大的或者相等的
            while (i <= j && a[i] < pv) {
                i++;
            }
            // j 从右向左找小的或者相等的
            while (i <= j && a[j] > pv) {
                j--;
            }
            if (i <= j) {
                swap(a, i, j);
                i++;
                j--;
            }
        }
        swap(a, j, left);
        return j;
    }

    private static void swap(int[] a, int i, int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

    public static void main(String[] args) {
//        int[] a = {4, 2, 1, 3, 2, 4}; // 最外层循环 = 要加
//        int[] a = {2, 1, 3, 2}; // 内层循环 = 要加
        int[] a = {2, 1, 3, 2}; // 内层if要加
        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println(Arrays.toString(a));
    }
}

 

 

8.计数排序

计数排序是一种非比较性的排序算法,适用于待排序元素范围较小的情况。它通过统计每个元素出现的次数,然后根据这些统计信息确定每个元素在排序后的位置。

基本步骤:

  1. 找到最大值,创建一个大小为 最大值+1 的 count 数组
  2. count 数组的索引对应原始数组的元素,用来统计该元素的出现次数
  3. 遍历 count 数组,根据 count 数组的索引(即原始数组的元素)以及出现次数,生成排序后内容,count 数组的索引是:已排序好的
  4. 前提:待排序元素 >=0 且最大值不能太大。

代码:

package com.dreams.sort;

import java.util.Arrays;

/**
 * 计数排序(处理 >= 0 的元素)
 */
public class CountingSortPositive {

    /*
        要点
        1. 找到最大值,创建一个大小为 最大值+1 的 count 数组
        2. count 数组的索引对应原始数组的元素,用来统计该元素的出现次数
        3. 遍历 count 数组,根据 count 数组的索引(即原始数组的元素)以及出现次数,生成排序后内容
            count 数组的索引是:已排序好的

        前提:待排序元素 >=0 且最大值不能太大
     */
    public static void main(String[] args) {
        int[] a = {5, 1, 1, 3, 0, -1};  // 5+1=6
        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println(Arrays.toString(a));
    }

    /*
        {5, 1, 1, 3, 0}  原始数组 a
        [1   2   0   1   0   1] count
         0   1   2   3   4   5
     */
    public static void sort(int[] a) {
        // 1. 准备 count 数组
        int max = a[0];
        for (int i = 1; i < a.length; i++) {
            if (a[i] > max) {
                max = a[i];
            }
        }
        int[] count = new int[max + 1];

        // 2. 用 count 数组记录各个元素出现次数,索引对应元素
        for (int v : a) { // v 原始数组的元素
            count[v]++;
        }

        // 3. 从 count 数组取出索引,即排好序的元素
        int k = 0;
        for (int i = 0; i < count.length; i++) {
            // i 代表原始数组元素 count[i] 元素出现次数
            while (count[i] > 0) {
                a[k++] = i;
                count[i]--;
            }
        }
    }
}

 

改进:

package com.dreams.sort;

import java.util.Arrays;

/**
 * 计数排序
 */
public class CountingSort {

    /*
        要点
        1. 让原始数组的最小值映射到 count[0] 最大值映射到 count 最右侧
        2. 原始数组元素 - 最小值 = count 索引
        3. count 索引 + 最小值 = 原始数组元素
     */
    public static void main(String[] args) {
        int[] a = {5, 1, 1, 3, 0, -1};
        System.out.println(Arrays.toString(a));
        sort(a);
        System.out.println(Arrays.toString(a));
    }

    // 2N + K         n*log(n)
    /*
        {5, 1, 1, 3, 0, -1}  原始数组 a

        [1   1   2   0   1   0   1 ] count
         0   1   2   3   4   5   6
         -1  0   1       3       5
     */
    public static void sort(int[] a) {
        int max = a[0];
        int min = a[0];
        for (int i = 1; i < a.length; i++) {
            if (a[i] > max) {
                max = a[i];
            }
            if (a[i] < min) {
                min = a[i];
            }
        }
        int[] count = new int[max - min + 1];

        for (int v : a) { // v 原始数组元素 - 最小值 = count 索引
            count[v - min]++;
        }
        //System.out.println(Arrays.toString(count));

        int k = 0;
        for (int i = 0; i < count.length; i++) {
            // i + min 代表原始数组元素 count[i] 元素出现次数
            while (count[i] > 0) {
                a[k++] = i + min;
                count[i]--;
            }
        }
    }
}

 

9.桶排序

桶排序是一种排序算法,它将待排序的元素分散到若干个“桶”中,然后对每个桶中的元素进行排序,最后按照桶的顺序以及每个桶内元素的顺序将它们合并成有序序列。

我们需要一个动态数组

package com.dreams.datastructure.array;

import java.util.Arrays;
import java.util.Iterator;
import java.util.function.Consumer;
import java.util.stream.IntStream;

/**
 * 动态数组
 */
public class DynamicArray implements Iterable<Integer> {
    private int size = 0; // 逻辑大小
    private int capacity = 8; // 容量
    private int[] array = {};

    public int[] array() {
        return Arrays.copyOf(array, size);
    }

    /**
     * 向最后位置 [size] 添加元素
     *
     * @param element 待添加元素
     */
    public void addLast(int element) {
        add(size, element);
    }

    /**
     * 向 [0 .. size] 位置添加元素
     *
     * @param index   索引位置
     * @param element 待添加元素
     */
    public void add(int index, int element) {
        checkAndGrow();

        // 添加逻辑
        if (index >= 0 && index < size) {
            // 向后挪动, 空出待插入位置
            System.arraycopy(array, index,
                    array, index + 1, size - index);
        }
        array[index] = element;
        size++;
    }

    private void checkAndGrow() {
        // 容量检查
        if (size == 0) {
            array = new int[capacity];
        } else if (size == capacity) {
            // 进行扩容, 1.5 1.618 2
            capacity += capacity >> 1;
            int[] newArray = new int[capacity];
            System.arraycopy(array, 0,
                    newArray, 0, size);
            array = newArray;
        }
    }

    /**
     * 从 [0 .. size) 范围删除元素
     *
     * @param index 索引位置
     * @return 被删除元素
     */
    public int remove(int index) { // [0..size)
        int removed = array[index];
        if (index < size - 1) {
            // 向前挪动
            System.arraycopy(array, index + 1,
                    array, index, size - index - 1);
        }
        size--;
        return removed;
    }


    /**
     * 查询元素
     *
     * @param index 索引位置, 在 [0..size) 区间内
     * @return 该索引位置的元素
     */
    public int get(int index) {
        return array[index];
    }

    /**
     * 遍历方法1
     *
     * @param consumer 遍历要执行的操作, 入参: 每个元素
     */
    public void foreach(Consumer<Integer> consumer) {
        for (int i = 0; i < size; i++) {
            // 提供 array[i]
            // 返回 void
            consumer.accept(array[i]);
        }
    }

    /**
     * 遍历方法2 - 迭代器遍历
     */
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            int i = 0;

            @Override
            public boolean hasNext() { // 有没有下一个元素
                return i < size;
            }

            @Override
            public Integer next() { // 返回当前元素,并移动到下一个元素
                return array[i++];
            }
        };
    }

    /**
     * 遍历方法3 - stream 遍历
     *
     * @return stream 流
     */
    public IntStream stream() {
        return IntStream.of(Arrays.copyOfRange(array, 0, size));
    }
}

 

桶排序代码:

桶内元素使用我们写过的插入排序即可。

package com.dreams.sort;

import com.dreams.datastructure.array.DynamicArray;

import java.util.Arrays;

/**
 * 桶排序
 */
public class BucketSort {
    public static void main(String[] args) {
        int[] ages = {20, 18, 28, 66, 25, 31, 67, 30}; // 假设人类年龄 1~99
        System.out.println(Arrays.toString(ages));
        sort(ages);
        System.out.println(Arrays.toString(ages));
    }

    /*
        0
        1   18
        2   20 25 28
        3   31
        4
        5
        6   66 67
        7
        8
        9
     */
    public static void sort(int[] ages) {
        // 1. 准备桶
        DynamicArray[] buckets = new DynamicArray[10];
        for (int i = 0; i < buckets.length; i++) {
            buckets[i] = new DynamicArray();
        }
        // 2. 放入年龄数据
        for (int age : ages) {
            buckets[age / 10].addLast(age);
        }
        int k = 0;
        for (DynamicArray bucket : buckets) {
            // 3. 排序桶内元素
            int[] array = bucket.array();
            InsertionSort.sort(array);
            System.out.println(Arrays.toString(array));
            // 4. 把每个桶排序好的内容,依次放入原始数组
            for (int v : array) {
                ages[k++] = v;
            }
        }
    }
}

 

代码改进:

主要是桶数量的优化。

package com.dreams.sort;

import com.dreams.datastructure.array.DynamicArray;

import java.util.Arrays;

/**
 * 桶排序(改进)
 */
public class BucketSortGeneric {
    public static void main(String[] args) {
        int[] ages = {9, 0, 5, 1, 3, 2, 4, 6, 8, 7};
        System.out.println(Arrays.toString(ages));
        int max = ages[0];
        int min = ages[0];
        for (int i = 1; i < ages.length; i++) {
            if (ages[i] > max) {
                max = ages[i];
            }
            if (ages[i] < min) {
                min = ages[i];
            }
        }
        // 1. 准备桶
        DynamicArray[] buckets = new DynamicArray[(max - min) / 3 + 1];
        for (int i = 0; i < buckets.length; i++) {
            buckets[i] = new DynamicArray();
        }
        // 2. 放入数据
        for (int age : ages) {
            buckets[(age - min) / 3].addLast(age);
        }
        int k = 0;
        for (DynamicArray bucket : buckets) {
            // 3. 排序桶内元素
            int[] array = bucket.array();
            InsertionSort.sort(array);
//            System.out.println(Arrays.toString(array));
            // 4. 把每个桶排序好的内容,依次放入原始数组
            for (int v : array) {
                ages[k++] = v;
            }
        }
        System.out.println(Arrays.toString(ages));
    }

    /*
        0   0,1,2
        1   3,4,5
        2   6,7,8
        3   9
     */
    public static void sort(int[] a, int range) {
        int max = a[0];
        int min = a[0];
        for (int i = 1; i < a.length; i++) {
            if (a[i] > max) {
                max = a[i];
            }
            if (a[i] < min) {
                min = a[i];
            }
        }
        // 1. 准备桶
        DynamicArray[] buckets = new DynamicArray[(max - min) / range + 1];
        for (int i = 0; i < buckets.length; i++) {
            buckets[i] = new DynamicArray();
        }
        // 2. 放入数据
        for (int age : a) {
            buckets[(age - min) / range].addLast(age);
        }
        int k = 0;
        for (DynamicArray bucket : buckets) {
            // 3. 排序桶内元素
            int[] array = bucket.array();
            InsertionSort.sort(array);
//            System.out.println(Arrays.toString(array));
            // 4. 把每个桶排序好的内容,依次放入原始数组
            for (int v : array) {
                a[k++] = v;
            }
        }
    }
}

 

 

10.基数排序

基数排序(Radix Sort)是一种非比较型的排序算法,它将整数按照位数切割成不同的数字,然后按每个位数进行排序。基数排序的实现可以使用计数排序或桶排序作为子排序算法。

代码:

这里考虑到字符存在

package com.dreams.sort;

import java.util.ArrayList;

/**
 * 基数排序 最低有效数字 LSD(Least significant digit)
 */
public class RadixSort {

    public static void radixSort(String[] a, int length) {
        // 1. 准备桶
        ArrayList<String>[] buckets = new ArrayList[128];
        for (int i = 0; i < buckets.length; i++) {
            buckets[i] = new ArrayList<>();
        }
        // 2. 进行多轮按位桶排序
        for (int i = length - 1; i >= 0; i--) {
            // 将字符串放入合适的桶
            for (String s : a) {
                buckets[s.charAt(i)].add(s);
            }
            // 重新取出排好序的字符串,放回原始数组
            int k = 0;
            for (ArrayList<String> bucket : buckets) {
                for (String s : bucket) {
                    a[k++] = s;
                }
                bucket.clear();
            }
//            System.out.println(Arrays.toString(a));
        }
    }

    public static void main(String[] args) {
        String[] phoneNumbers = new String[10];  // 0~127
        phoneNumbers[0] = "13812345678";  // int long
        phoneNumbers[1] = "13912345678";
        phoneNumbers[2] = "13612345678";
        phoneNumbers[3] = "13712345678";
        phoneNumbers[4] = "13512345678";
        phoneNumbers[5] = "13412345678";
        phoneNumbers[6] = "15012345678";
        phoneNumbers[7] = "15112345678";
        phoneNumbers[8] = "15212345678";
        phoneNumbers[9] = "15712345678";

        /*String[] phoneNumbers = new String[6];
        phoneNumbers[0] = "158";
        phoneNumbers[1] = "151";
        phoneNumbers[2] = "235";
        phoneNumbers[3] = "138";
        phoneNumbers[4] = "139";
        phoneNumbers[5] = "159";*/

        /*
            0
            1   151
            2
            3
            4
            5   135
            6
            7
            8   158 138
            9   139 159
            151 135 158 138 139 159  按个位排

            0
            1
            2
            3   135 138 139
            4
            5   151 158 159
            6
            7
            8
            9
            135 138 139 151 158 159  按十位排
         */

        RadixSort.radixSort(phoneNumbers, 11);
        for (String phoneNumber : phoneNumbers) {
            System.out.println(phoneNumber);
        }
    }
}

 

11.java排序

 

参考

黑马数据结构

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇