详解Java常用排序算法(10种,含演示动画)

Java常用的排序算法有以下几种:

  1. 冒泡排序(Bubble Sort)
  2. 选择排序(Selection Sort)
  3. 插入排序(Insertion Sort)
  4. 希尔排序(Shell Sort)
  5. 归并排序(Merge Sort)
  6. 快速排序(Quick Sort)
  7. 堆排序(Heap Sort)
  8. 计数排序(Counting Sort)
  9. 桶排序(Bucket Sort)
  10. 基数排序(Radix Sort)

这些排序算法都有各自的优缺点,应根据具体情况选择适合的算法。

算法复杂度

排序算法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性
冒泡排序O(n^2)O(n^2)O(n)O(1)稳定
选择排序O(n^2)O(n^2)O(n^2)O(1)不稳定
插入排序O(n^2)O(n^2)O(n)O(1)稳定
希尔排序O(n log n)O(n^2)O(n)O(1)不稳定
归并排序O(n log n)O(n log n)O(n log n)O(n)稳定
快速排序O(n log n)O(n^2)O(n log n)O(log n)不稳定
堆排序O(n log n)O(n log n)O(n log n)O(1)不稳定
计数排序O(n+k)O(n+k)O(n+k)O(k)稳定
桶排序O(n^2)O(n^2)O(n)O(n+k)稳定
基数排序O(n*k)O(n*k)O(n*k)O(n+k)稳定

其中,n表示输入元素的数量,k表示元素的取值范围大小。

  • 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
  • 不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
  • 时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
  • 空间复杂度:是指算法在计算机 内执行时所需存储空间的度量,它也是数据规模n的函数。

1. 冒泡排序(Bubble Sort)

冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就交换位置。这个过程持续对数列的末尾进行,直到整个数列都排序完成。

在这里插入图片描述

import org.junit.jupiter.api.Assertions;
import java.util.Arrays;public class Bubble {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]) {// 交换arr[j+1]和arr[j]int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}}public static void main(String[] args) {int[] arr = {5, 2, 8, 3, 1, 6};int[] expectedArr = {1, 2, 3, 5, 6, 8};Bubble.bubbleSort(arr);System.out.println("arr = " + Arrays.toString(arr));Assertions.assertArrayEquals(expectedArr, arr);}
}

在上面的代码中, bubbleSort 函数接受一个整数数组作为输入,并使用冒泡排序算法对其进行排序。外部循环运行 n - 1 次,其中 n 是数组的长度,内部循环运行 n - i - 1 次,其中 i 是外部循环的当前迭代次数。这是因为在每次外部循环迭代后,最大的元素肯定在数组的末尾,因此我们不需要再次比较它。

在内部循环中,我们比较相邻的元素并交换它们,如果它们的顺序不正确。这样,最大的元素就“冒泡”到数组的末尾。在每次遍历数组后,最大的元素都处于它的最终位置,因此我们可以将内部循环的大小减少1。

冒泡排序的时间复杂度为O(n^2),这使得它在大型列表和实际应用中效率低下。但是,由于其简单性,它是向初学者教授排序的好算法。

2. 选择排序(Selection Sort)

选择排序是一种简单的排序算法,它的基本思想是每次从待排序的元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的元素排完。

在这里插入图片描述

import org.junit.jupiter.api.Assertions;
import java.util.Arrays;public class Selection {public static void selectionSort(int[] arr) {int n = arr.length;for (int i = 0; i < n - 1; i++) {int minIndex = i;for (int j = i + 1; j < n; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}// 交换arr[i]和arr[minIndex]int temp = arr[i];arr[i] = arr[minIndex];arr[minIndex] = temp;}}public static void main(String[] args) {int[] arr = {5, 2, 8, 3, 1, 6};int[] expectedArr = {1, 2, 3, 5, 6, 8};Selection.selectionSort(arr);System.out.println("arr = " + Arrays.toString(arr));Assertions.assertArrayEquals(expectedArr, arr);}
}

在上面的代码中, selectionSort 函数接受一个整数数组作为输入,并使用选择排序算法对其进行排序。外部循环运行 n - 1 次,其中 n 是数组的长度。在内部循环中,我们找到未排序部分中的最小元素,并将其索引存储在 minIndex 变量中。然后,我们将最小元素与已排序部分的末尾交换。在每次外部循环迭代后,已排序部分的长度增加1,未排序部分的长度减少1。

选择排序的时间复杂度为O(n^2),这使得它在大型列表和实际应用中效率低下。但是,由于其简单性,它是向初学者教授排序的好算法。

3. 插入排序(Insertion Sort)

插入排序是一种简单的排序算法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。

在这里插入图片描述

import org.junit.jupiter.api.Assertions;
import java.util.Arrays;public class Insertion {public static void insertionSort(int[] arr) {int n = arr.length;// 外部循环从第二个元素开始,// 因为我们将第一个元素视为已排序部分for (int i = 1; i < n; i++) {int key = arr[i];int j = i - 1;// 将当前值key和前面的值进行比较,// 如果前面的值>key 则将值往后移1位while (j >= 0 && arr[j] > key) {arr[j + 1] = arr[j];j--;}// 在不小当前值key的位置,插入当前值keyarr[j + 1] = key;}}public static void main(String[] args) {int[] arr = {5, 2, 8, 3, 1, 6};int[] expectedArr = {1, 2, 3, 5, 6, 8};Insertion.insertionSort(arr);System.out.println("arr = " + Arrays.toString(arr));Assertions.assertArrayEquals(expectedArr, arr);}
}

在上面的代码中, insertionSort 函数接受一个整数数组作为输入,并使用插入排序算法对其进行排序。外部循环从第二个元素开始,因为我们将第一个元素视为已排序部分。在内部循环中,我们将要插入的元素与已排序部分的元素进行比较,如果要插入的元素小于已排序部分的元素,则将已排序部分的元素向右移动一位,以便为要插入的元素腾出空间。在内部循环结束后,我们将要插入的元素插入到正确的位置。在每次外部循环迭代后,我们可以确保前i个元素已经被排序。

插入排序的时间复杂度为O(n^2),这使得它在大型列表和实际应用中效率低下。但是,插入排序的实现非常简单,它在小型列表上的性能非常好。

4. 希尔排序(Shell Sort)

希尔排序是一种改进的插入排序算法,它的基本思想是将待排序的数组按照一定的间隔进行分组,对每组使用插入排序算法进行排序,然后缩小间隔,再对分组进行排序,直到间隔为1为止。

逐渐减小间隔大小的方法有助于提高排序过程的效率,可以减少比较和交换的次数。这是希尔排序算法的一个关键特点。

在这里插入图片描述

import org.junit.jupiter.api.Assertions;
import java.util.Arrays;public class Shell {public static void shellSort(int[] arr) {int n = arr.length;// 初始化间隔(gap)的值,它决定了每次迭代中子数组的大小// 从数组长度的一半开始作为初始间隔值,gap就是分割的子数组数量for (int gap = n / 2; gap > 0; gap /= 2) {// 循环从间隔值开始,遍历数组直到数组的末尾;代表循环所有的子数组for (int i = gap; i < n; i++) {int temp = arr[i];int j = i;// 将当前元素 arr[j] 的值替换为前一个元素 arr[j - gap] 的值。// 通过这个操作,将较大的元素向后移动,为当前元素腾出位置while (j >= gap && arr[j - gap] > temp) {arr[j] = arr[j - gap];j -= gap;}arr[j] = temp;}}}public static void main(String[] args) {int[] arr = {5, 2, 8, 3, 1, 6};int[] expectedArr = {1, 2, 3, 5, 6, 8};Shell.shellSort(arr);System.out.println("arr = " + Arrays.toString(arr));Assertions.assertArrayEquals(expectedArr, arr);}
}

在上面的代码中, shellSort 函数接受一个整数数组作为输入,并使用希尔排序算法对其进行排序。外部循环使用一个间隔变量 gap ,初始值为数组长度的一半,每次循环将 gap 除以2,直到 gap 为1。内部循环从第 gap 个元素开始,将要插入的元素与已排序部分的元素进行比较,如果要插入的元素小于已排序部分的元素,则将已排序部分的元素向右移动 gap 个位置,以便为要插入的元素腾出空间。在内部循环结束后,我们将要插入的元素插入到正确的位置。在每次外部循环迭代后,我们可以确保数组的前 gap 个元素已经被排序。

希尔排序的时间复杂度为O(n^2),但实际上它的性能比插入排序要好得多,特别是在大型列表上。希尔排序的性能取决于间隔序列的选择,但是目前还没有一种最优的间隔序列。

5. 归并排序(Merge Sort)

归并排序是一种分治思想的排序算法,它的基本思想是将待排序的数组分成若干个子序列,每个子序列都是有序的,然后再将子序列合并成一个有序的数组。

在这里插入图片描述

import org.junit.jupiter.api.Assertions;
import java.util.Arrays;public class Merge {public static void main(String[] args) {int[] arr = {5, 2, 8, 3, 1, 6};int[] expectedArr = {1, 2, 3, 5, 6, 8};Merge.mergeSort(arr, 0, arr.length - 1);System.out.println("arr = " + Arrays.toString(arr));Assertions.assertArrayEquals(expectedArr, arr);}public static void mergeSort(int[] arr, int left, int right) {if (left < right) {int mid = (left + right) / 2;mergeSort(arr, left, mid);mergeSort(arr, mid + 1, right);merge(arr, left, mid, right);}}public static void merge(int[] arr, int left, int mid, int right) {// 子数组 L 的大小int n1 = mid - left + 1;// 右子数组 R 的大小int n2 = right - mid;// 创建两个临时数组 L 和 R ,分别用来存储左子数组和右子数组的元素int[] L = new int[n1];int[] R = new int[n2];// 使用 for 循环将原始数组 arr 中的元素复制到临时数组 L 和 R 中,分别从 left 和 mid + 1 开始for (int i = 0; i < n1; i++) {L[i] = arr[left + i];}for (int j = 0; j < n2; j++) {R[j] = arr[mid + 1 + j];}// 初始化三个变量 i、j和k,分别指向数组 L 、R 和原始数组 arr 的起始位置int i = 0, j = 0, k = left;// 使用 while 循环,比较 L 和 R 的元素,并将较小的元素放回原始数组 arr 中while (i < n1 && j < n2) {if (L[i] <= R[j]) {arr[k] = L[i];i++;} else {arr[k] = R[j];j++;}k++;}// 当 L 或 R 中的元素用完时,将剩余的元素依次放回原始数组 arr 中while (i < n1) {arr[k] = L[i];i++;k++;}while (j < n2) {arr[k] = R[j];j++;k++;}// merge 方法执行完毕后,两个子数组范围内的元素已经按照从小到大的顺序合并到了原始数组 arr 中}
}

在上面的代码中, mergeSort 函数接受一个整数数组、一个左索引和一个右索引作为输入,并使用归并排序算法对指定范围内的数组元素进行排序。该函数使用递归将数组分成两个子数组,然后对它们进行排序,并最后将它们合并成一个有序数组。 merge 函数用于将两个有序数组合并成一个有序数组。它创建两个临时数组 L 和 R ,将左子数组的元素存储在 L 中,将右子数组的元素存储在 R 中,然后将它们合并成一个有序数组并存储在原始数组中。

归并排序的时间复杂度为O(nlogn),它的性能比冒泡排序和插入排序要好得多,特别是在大型列表上。

6. 快速排序(Quick Sort)

快速排序是一种分治思想的排序算法,它的基本思想是通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,然后再分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

在这里插入图片描述

import org.junit.jupiter.api.Assertions;
import java.util.Arrays;public class Quick {public static void main(String[] args) {int[] arr = {5, 2, 8, 6, 1, 3};int[] expectedArr = {1, 2, 3, 5, 6, 8};Quick.quickSort(arr, 0, arr.length - 1);System.out.println("arr = " + Arrays.toString(arr));Assertions.assertArrayEquals(expectedArr, arr);}// 接收一个数组 arr,一个低索引 low ,和一个高索引 high 作为参数public static void quickSort(int[] arr, int low, int high) {// 检查 low 是否小于 high。如果不是,则意味着数组只有一个元素或为空,因此不需要排序if (low < high) {int pivot = partition(arr, low, high);quickSort(arr, low, pivot - 1);quickSort(arr, pivot + 1, high);}}/*** 取最后一个元素作为枢轴元素,将较小的元素放在左边,较大的元素放在右边* @param arr 输入数组* @param low 低位索引* @param high 高位索引* @return 枢轴所在位置*/private static int partition(int[] arr, int low, int high) {// 将最后一个元素作为枢轴元素( arr[high] )int pivot = arr[high];// 将 i 初始化为 low - 1,用于跟踪较小元素的索引int i = low - 1;for (int j = low; j < high; j++) {if (arr[j] < pivot) {// 如果当前元素 arr[j] 小于枢轴元素,则增加 i 并交换 arr[i] 和 arr[j]// 较小元素索引+1i++;// 将当前元素 arr[j] 放在较小元素索引位置// 将较小元素放在前面swap(arr, i, j);}// 其他情况,则较小元素索引没有增加,说明当前元素应该放在右边}// 将枢轴元素( arr[high] )与索引 i + 1 处的元素交换。// 确保枢轴元素左边是较小元素,右边是较大元素swap(arr, i + 1, high);// 将 i + 1 作为枢轴索引返回return i + 1;}private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}
}

在上面的代码中, quickSort 函数接受一个整数数组、一个低索引和一个高索引作为输入,并使用快速排序算法对指定范围内的数组元素进行排序。该函数使用递归将数组分成两个子数组,然后对它们进行排序,并最后将它们合并成一个有序数组。 partition 函数用于将数组分成两个子数组。它选择数组中的最后一个元素作为基准元素,然后将小于基准元素的元素放在左边,将大于基准元素的元素放在右边,并返回基准元素的索引。 swap 函数用于交换数组中的两个元素。

快速排序的时间复杂度为O(nlogn),它的性能比冒泡排序和插入排序要好得多,特别是在大型列表上。

7. 堆排序(Heap Sort)

堆排序是一种树形选择排序算法,它的基本思想是将待排序的数组构建成一个大根堆(或小根堆),然后将堆顶元素与堆底元素交换位置,再将剩余元素重新构建成堆,重复执行交换和重构堆的操作,直到整个数组有序。

堆排序是一种基于堆数据结构的排序算法,它的时间复杂度为O(nlogn)。

堆的概念

集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1(左节点) 且 Ki<=K2i+2(右节点),则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。完全二叉树(除了最后一层以外上面的节点但是非空的,最后一层节点是从左到右依次排布的)

堆的性质

1.堆中某个节点的值总是不大于或不小于其父节点的值;
2.堆总是一棵完全二叉树。

完全二叉树

完全二叉树的特点:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。需要注意的是,满二叉树肯定是完全二叉树,而完全二叉树不一定是满二叉树。

  • 完全二叉树的第 i 层至多有 2^i - 1 个结点。
  • 完全二叉树的第 i 层至少有 2^(i - 1) 个结点。
  • 完全二叉树的叶子结点只出现在最底层和次底层。
  • 完全二叉树每一层的结点个数都达到了最大值

在这里插入图片描述

public class Heap {// 堆排序方法public static void heapSort(int[] arr) {int n = arr.length;// 构建大根堆,// 这段代码是构建大根堆的过程,它的循环次数为n/2-1次,是因为在完全二叉树中,叶子节点不需要进行堆化操作,// 所以只需要对非叶子节点进行堆化,而非叶子节点的数量为n/2-1个。因此,只需要循环n/2-1次即可完成大根堆的构建。// 非叶子节点在一维数组中就是前面 n/2-1for (int i = n / 2 - 1; i >= 0; i--) {// 从最底层的根节点开始堆化,每次执行完成后,都找出最大值,并放在根节点位置// 逐层往上找,循环结束后,第一个元素肯定是最大值heapify(arr, n, i);}// 依次取出堆顶元素,并将余下元素继续堆化,得到有序序列for (int i = n - 1; i >= 0; i--) {// 第一个for循环已经找出最大值,所以先做交货,把最大值换到最后一个位置// 把最大值交换到最后一个位置,下一次循环最后一个位置就不比较了swap(arr, 0, i);// 继续找出最大值,放在第一个位置heapify(arr, i, 0);}}private static void heapify(int[] arr, int heapSize, int i) {int largest = i; // 初始化假设最大值为根节点int left = 2 * i + 1; // 相对于索引i的左节点索引int right = 2 * i + 2; // 相对于索引i的右节点索引// 找到左右子节点中的最大值if (left < heapSize && arr[left] > arr[largest]) {// 如果有左节点,且左节点大于根节点,则记录左节点为最大值largest = left;}if (right < heapSize && arr[right] > arr[largest]) {// 如果有右节点,且右节点大于最大值,则记录右节点为最大值largest = right;}// 上面两个if之后,肯定找到最大值if (largest != i) {// i 是根节点下标// 如果最大值不是根节点,则交换根节点与最大值节点,// 并递归地对最大值节点进行堆化swap(arr, i, largest);heapify(arr, heapSize, largest);}}private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}public static void main(String[] args) {int[] arr = {5, 2, 8, 3, 12, 35, 57, 1, 6};int[] expectedArr = {1, 2, 3, 5, 6, 8, 12, 35, 57};Heap.heapSort(arr);System.out.println("arr = " + Arrays.toString(arr));Assertions.assertArrayEquals(expectedArr, arr);}
}

在上面的代码中, heapSort 函数接受一个整数数组作为输入,并使用堆排序算法对该数组进行排序。该函数首先构建一个大根堆,然后依次取出堆顶元素,得到有序序列。 heapify 函数用于对一个节点进行堆化操作。它接受三个参数:待堆化的数组、数组的大小和要堆化的节点的索引。该函数首先找到左右子节点中的最大值,如果最大值不是根节点,则交换根节点与最大值节点,并递归地对最大值节点进行堆化。 swap 函数用于交换数组中的两个元素。

8. 计数排序(Counting Sort)

计数排序是一种非比较排序算法,它的基本思想是统计数组中每个元素出现的次数,然后根据元素出现的次数依次将元素放入有序的数组中。

计数排序时间复杂度为O(n+k),其中k为待排序的元素的最大值。

在这里插入图片描述

import org.junit.jupiter.api.Assertions;
import java.util.Arrays;public class Counting {public static void countingSort(int[] arr) {int n = arr.length;// 取出数组中最大值int max = getMax(arr);int[] count = new int[max + 1];// 统计每个元素出现的次数for (int i = 0; i < n; i++) {count[arr[i]]++;}// 计算每个元素在有序序列中的位置for (int i = 1; i <= max; i++) {// 因为count包含了每个数据出现的次数,所以从小到大,// 逐个往前加得到就是原数组中每个元素在有序序列中应有的位置count[i] += count[i - 1];}// 输出有序序列int[] sortedArr = new int[n];for (int i = n - 1; i >= 0; i--) {int item = arr[i];//元素int itemPos = count[item];// 元素在有序数组中的位置sortedArr[itemPos - 1] = item; // 将元素填入有序数组count[item]--;}// 将有序序列复制回原数组System.arraycopy(sortedArr, 0, arr, 0, n);}private static int getMax(int[] arr) {int max = arr[0];for (int i = 1; i < arr.length; i++) {if (arr[i] > max) {max = arr[i];}}return max;}public static void main(String[] args) {int[] arr = {5, 2, 6, 8, 3, 1, 6, 5, 12};int[] expectedArr = {1, 2, 3, 5, 5, 6, 6, 8, 12};Counting.countingSort(arr);System.out.println("arr = " + Arrays.toString(arr));Assertions.assertArrayEquals(expectedArr, arr);}
}

9. 桶排序(Bucket Sort)

桶排序是一种非比较排序算法,它的基本思想是将待排序的数组分到有限数量的桶里,然后对每个桶进行排序,最后依次将所有桶中的元素取出来,组成有序的数组。

桶排序的时间复杂度为O(n),其中n为待排序元素的个数。

在这里插入图片描述

import org.junit.jupiter.api.Assertions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;public class Bucket {public static void main(String[] args) {int[] arr = {5, 2, 8, 3, 12, 35, 57, 1, 6};int[] expectedArr = {1, 2, 3, 5, 6, 8, 12, 35, 57};Bucket.bucketSort(arr, 20);System.out.println("arr = " + Arrays.toString(arr));Assertions.assertArrayEquals(expectedArr, arr);}/*** 桶排序* @param arr 待排序数组* @param bucketSize 桶大小,数据不宜过大,桶越大,后续对桶内数据排序越耗时*/public static void bucketSort(int[] arr, int bucketSize) {if (arr.length == 0) {return;}// 循环数组,先找到最小值和最大值int minValue = arr[0];int maxValue = arr[0];for (int i = 1; i < arr.length; i++) {if (arr[i] < minValue) {minValue = arr[i];} else if (arr[i] > maxValue) {maxValue = arr[i];}}// 根据桶的大小,计算桶个数,并初始化桶int bucketCount = (maxValue - minValue) / bucketSize + 1;List<List<Integer>> buckets = new ArrayList<>(bucketCount);for (int i = 0; i < bucketCount; i++) {buckets.add(new ArrayList<>());}for (int i = 0; i < arr.length; i++) {int bucketIndex = (arr[i] - minValue) / bucketSize;buckets.get(bucketIndex).add(arr[i]);}int currentIndex = 0;for (int i = 0; i < bucketCount; i++) {List<Integer> bucket = buckets.get(i);// 对桶内数据进行排序Collections.sort(bucket);// 将数据逐个从桶内取出,并存入数组for (int j = 0; j < bucket.size(); j++) {arr[currentIndex++] = bucket.get(j);}}}}

在上面的代码中, bucketSort 函数接受一个整数数组和桶的大小作为输入,并使用桶排序算法对该数组进行排序。该函数首先找到输入数组中的最小值和最大值,并计算桶的个数。然后,该函数创建一个大小为桶的个数的桶列表,用于存储每个桶中的元素。接下来,该函数依次遍历输入数组,将每个元素放入相应的桶中。然后,该函数对每个桶中的元素进行排序,并将排序后的元素按顺序合并起来得到有序序列。

10. 基数排序(Radix Sort)

基数排序是一种非比较排序算法,它的基本思想是将待排序的数组按照位数(个位、十位、百位)进行划分,然后依次对每个位上的数字进行排序,最终得到有序的数组。

基数排序的时间复杂度为O(d(n+k)),其中d为最大元素的位数,n为待排序元素的个数,k为桶的个数。

在这里插入图片描述

public class Radix {public static void radixSort(int[] arr) {if (arr.length == 0) {return;}// 循环取得数组中的最大值int maxNum = arr[0];for (int i = 1; i < arr.length; i++) {if (arr[i] > maxNum) {maxNum = arr[i];}}// 根据最大值算出数组中的最大位数,个位、十位、百位、千位等int maxDigit = 0;while (maxNum != 0) {maxNum /= 10;maxDigit++;}// 初始化10个list,分别存放位数是0-9的10组数字List<List<Integer>> buckets = new ArrayList<>(10);for (int i = 0; i < 10; i++) {buckets.add(new ArrayList<>());}int mod = 10; // 初始10,用于数据个位数取模int div = 1; // 桶序号除数// 按位数循环数组,个位循环1次,十位循环2次,百位循环3次,以此类推!for (int i = 0; i < maxDigit; i++, mod *= 10, div *= 10) {// 循环数组,将数据分别存入桶中// 第一次循环,桶里面的个位数顺序排序完成// 第二次循环,个位、十位都排序完成// 第三次循环,个位、十位、百位都排序完成for (int j = 0; j < arr.length; j++) {// 计算当前位数的桶序号int bucketIndex = (arr[j] % mod) / div;buckets.get(bucketIndex).add(arr[j]);}// 循环桶列表,将当前位数已排序的数据放入数组中int currentIndex = 0;for (int j = 0; j < 10; j++) {List<Integer> bucket = buckets.get(j);for (int k = 0; k < bucket.size(); k++) {arr[currentIndex++] = bucket.get(k);}bucket.clear();}}}public static void main(String[] args) {int[] arr = {5, 2, 8, 3, 12, 35, 57, 1, 6};int[] expectedArr = {1, 2, 3, 5, 6, 8, 12, 35, 57};Radix.radixSort(arr);System.out.println("arr = " + Arrays.toString(arr));Assertions.assertArrayEquals(expectedArr, arr);}
}

在上面的代码中, radixSort 函数接受一个整数数组作为输入,并使用基数排序算法对该数组进行排序。该函数首先找到输入数组中的最大值,并计算最大值的位数。然后,该函数创建一个大小为10的桶列表,用于存储每个桶中的元素。接下来,该函数依次遍历输入数组的每一位,将每个元素放入相应的桶中。然后,该函数将每个桶中的元素按顺序合并起来得到有序序列。

参考

  • https://github.com/byteflying/AlgorithmManRelease

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/754584.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Studio:你的主机中的软件终止了一个已建立的连接

我不喜欢等人也不喜欢被别人等——赤砂之蝎 一、提出问题 二、分析问题 搜索网上的教程尝试解决 1、任务管理器结束adb进程无用 2、电脑没有开启热点排除热点问题 3、校园网切换到热点 4、项目重新解压打开 5、更换国内镜像源 上述方法全部无法解决问题 分析问题原因在于之前A…

Github 2024-03-18 开源项目周报Top15

根据Github Trendings的统计,本周(2024-03-18统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目6TypeScript项目2Go项目2JavaScript项目2非开发语言项目1HTML项目1CSS项目1Rust项目1Dart项目1C++项目1Jupyter Notebook项目1Electr…

JVM中对象创建过程

在JVM中对象的创建&#xff0c;我们从一个new指令开始&#xff1a; 这个过程大概图示如下&#xff1a; 虚拟机收到new指令触发。 类加载检查&#xff1a;如果类没有被类加载器加载&#xff0c;则执行类加载流程&#xff08;将class信息加载到JVM的运行时数据区的过程&#xff…

Stable Diffusion + Segment Anything试用

安装 从continue-revolution/sd-webui-segment-anything安装插件分割模型下载后放到这个位置&#xff1a;${sd-webui}/extension/sd-webui-segment-anything/models/sam下&#xff0c;可以下载3个不同大小的模型&#xff0c;从大到小如下&#xff1a;vit_h is 2.56GB, vit_l i…

Java后端八股----JVM篇

上图中线程1&#xff0c;2如果资源被抢占了&#xff0c;则程序计数器记录一下执行的行号&#xff0c;等到资源就绪后会从记录的行号继续向后执行。 Java8把静态变量以及常量放到了线程的本地内存原空间中(避免放在堆中不可控)。 &#x1f446;图中第二种情况不太容易出现…

Linux docker2--镜像及容器操作-nginx部署示例

一、上一篇已经完成了docker的基础环境搭建&#xff0c;和docker的安装。不清楚的小伙伴可以自己找上一篇看一下。本例以部署nginx为例展示 二、镜像相关 1、切换docker的镜像源为阿里云 命令&#xff1a; sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <&l…

敏捷开发——elementUI/Vue使用/服务器部署

1. 创建vue项目 2. 安装element-ui组件库 npm i -S element-ui或 npm install element-ui3. 在main.js中导入element-ui组件 import ElementUI from element-ui import element-ui/lib/theme-chalk/index.css Vue.use(ElementUI)4. 运行 npm run serve后可以使用 ctrc终止进…

如何选择合适的数据可视化工具?

如果是入门级的数据可视化工具&#xff0c;使用Excel插件就足够了&#xff01; Excel插件&#xff0c;tusimpleBI 是一款 Excel 图表插件&#xff0c;提供超过120项图表功能&#xff0c;帮助用户制作各种 Excel 所没有的高级图表&#xff0c;轻轻松松一键出图。 它能够制作10…

VUE3 组件通信

props 用途&#xff1a;可以实现父子组件、子父组件、甚至兄弟组件通信 父组件 <template><div><Son :money"money"></Son></div> </template><script setup lang"ts"> import Son from ./son.vue import { re…

量子加速超算简介

量子加速超算简介 有用的量子计算的发展是全球政府、企业和学术界的巨大努力。 量子计算的优势可以帮助解决世界上一些与材料模拟、气候建模、风险管理、供应链优化和生物信息学等应用相关的最具挑战性的问题。 要实现量子计算的优势&#xff0c;需要将量子计算机集成到现有的…

容器部署对比:通用容器部署 vs 使用腾讯云容器镜像服务(TCR)部署 Stable Diffusion

目录 引言1 通用容器部署的主要步骤1.1 准备环境1.2 构建 Docker 镜像1.3 上传镜像1.4 部署容器1.5 配置网络1.6 监控和维护 2 使用腾讯云容器镜像服务&#xff08;TCR&#xff09;部署的主要步骤2.1 下载 Stable Diffusion web UI 代码2.2 制作 Docker 镜像2.3 上传镜像到 TCR…

Scala--02--IDEA编写Hello World

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.Scala 插件安装1&#xff09;插件离线安装步骤2&#xff09; 插件在线安装&#xff08;推荐可选&#xff09; 2.HelloWorld 案例1&#xff09;打开 IDEA->点击…

Javaweb学习记录(三)请求响应案例

下面为一个请求响应案例&#xff0c;postman发送请求&#xff0c;服务器响应将一个xml文件中的数据通过读取解析&#xff0c;将其用Result类标准的格式返回前端&#xff0c;在前端用json的方式显示 后端Controller代码 1、通过本类的字节码文件得到类加载器并寻找到需要解析的…

Jenkins-pipeline流水线构建完钉钉通知

添加钉钉机器人 在钉钉群设置里添加机器人拿出Webhook地址&#xff0c;设置关键词 Jenkins安装钉钉插件 Dashboard > 系统管理 > 插件管理&#xff0c;搜索构建通知&#xff0c;直接搜索Ding Talk也行 安装DingTalk插件&#xff0c;重启Jenkins 来到Dashboard > 系…

汽车专业翻译应该如何进行呢?

随着全球汽车行业的不断发展&#xff0c;大量的汽车业相关技术资料、产品说明、会议交流、推广分享等都需要进行语言转换&#xff0c;进而促进了汽车翻译业务的需求旺盛。那么&#xff0c;汽车专业翻译应该如何进行呢&#xff0c;北京哪个翻译公司比较好&#xff1f; 业内人士指…

Webpack学习记录

记录学习笔记&#xff0c;欢迎指正 1.大型项目为什么需要打包 1.1 使用打包工具原因 编译或转译文件&#xff1a; 项目中可能用到ES6语法&#xff0c;可能有浏览器不支持。需要打包工具将代码编译输出为ES5语法的代码。项目中可能使用Sass&#xff0c;Less等预处理器&#xff…

【Selenium(一)】

简介 Selenium是一个开源的自动化测试工具&#xff0c;主要用于Web应用程序的自动化测试。它支持多种浏览器&#xff0c;包括Chrome、Firefox、Internet Explorer等&#xff0c;以及多种编程语言&#xff0c;如Java、Python、C#、Ruby等&#xff0c;使得它成为Web自动化测试中…

python(Django)自动化之链接数据库

1、安装数据库 具体安装mysql的教程其实在csdn上都有具体教程大家可以看看 2、更换django的配置 具体是在settings.py中将代码更改成如下&#xff1a; DATABASES {default: {ENGINE: django.db.backends.mysql,NAME: autotest,USER: root,PASSWORD: chengxian8.8,HOST: 12…

腾讯云Kubernetes的容器服务平台TKE以及函数计算服务云感受

目录 一、整体结构 二、内容深度 三、技术实用性 一、容器技术 1. 腾讯自身 2. 美团 二、函数计算技术 1. 滴滴出行 2. 小红书 实际应用 容器技术实践示例 函数计算技术实践示例 高级技术探讨示例 书中感受 这边文章是对《2023腾讯云容器和函数计算技术实践精选集…

【iOS】——Blocks

文章目录 前言一、Blocks概要1.什么是Blocks 二、Block模式1.block语法2.block类型变量3.截获自动变量值4._Block修饰符5.截获的自动变量 三、Blocks的实现1.Block的实质2.截获自动变量值3._Block说明符4.Block存储域 前言 一、Blocks概要 1.什么是Blocks Blocks是C语言的扩…