当前位置:   article > 正文

Java 十大算法: 01、冒泡排序 02、选择排序 03、插入排序 04、希尔排序 05、归并排序 06、快速排序 07、堆排序 08、计数排序 09、桶排序 10、基数排序_java十大算法

java十大算法

 

 

01、冒泡排序

冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

作为最简单的排序算法之一,冒泡排序给我的感觉就像 Abandon 在单词书里出现的感觉一样,每次都在第一页第一位,所以最熟悉。冒泡排序还有一种优化算法,就是立一个 flag,当在一趟序列遍历中元素没有发生交换,则证明该序列已经有序。但这种改进对于提升性能来说并没有什么太大作用。 

1)算法步骤

  • 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  • 针对所有的元素重复以上的步骤,除了最后一个。
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较

  1. public class BubbleSort {
  2. /**
  3. * 冒泡排序:
  4. *
  5. * 1)算法步骤
  6. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  7. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  8. 针对所有的元素重复以上的步骤,除了最后一个。
  9. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
  10. * @param args
  11. */
  12. public static void main(String[] args) {
  13. int[] sourceArray ={3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};
  14. sort(sourceArray);
  15. }
  16. private static int[] sort(int[] sourceArray) {
  17. //对数组进去拷贝, 不改变参数内容
  18. int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
  19. for (int i = 1; i < arr.length; i++) {
  20. //设定一个标记, 若为true, 则表示此循环没有进行交换, 也就是待排序已经有序, 排序已经完成.
  21. boolean flag = true;
  22. for (int j = 0; j < arr.length-i; j++) {
  23. //相邻两个数俩俩对比
  24. if (arr[j] > arr[j+1]) {
  25. //对比得到最大的数
  26. int tmp = arr[j];
  27. //对比得到最小的数, 放在前面
  28. arr[j] = arr[j+1];
  29. //对比得到最大的数, 放在后面
  30. arr[j+1] = tmp;
  31. flag = false;
  32. }
  33. }
  34. if (flag) {
  35. break;
  36. }
  37. }
  38. System.out.println("冒泡排序: "+Arrays.toString(arr));
  39. return arr;
  40. }
  41. }

 

 

02、选择排序 

选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n²) 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。

1)算法步骤

  • 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
  • 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  • 重复第二步,直到所有元素均排序完毕。

  1. public class SelectionSort {
  2. /**
  3. * 选择排序:
  4. *
  5. * 1)算法步骤
  6. 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
  7. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  8. 重复第二步,直到所有元素均排序完毕。
  9. * @param args
  10. */
  11. public static void main(String[] args) {
  12. int[] sourceArray ={3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};
  13. sort(sourceArray);
  14. }
  15. private static int[] sort(int[] sourceArray) {
  16. //对数组进去拷贝, 不改变参数内容
  17. int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
  18. //总共要经过N-1轮比较
  19. for (int i = 0; i < arr.length-1; i++) {
  20. int min = i;
  21. //每轮需要比较的次数N-i
  22. for (int j = i+1; j < arr.length; j++) {
  23. if (arr[j] < arr[min]) {
  24. //记录目前能找到的最小值元素的下标
  25. min = j;
  26. }
  27. }
  28. //将找到的最小值和i位置所在的值进行交换
  29. if (i != min) {
  30. int tmp = arr[i];
  31. arr[i] = arr[min];
  32. arr[min] = tmp;
  33. }
  34. }
  35. System.out.println("选择排序: "+Arrays.toString(arr));
  36. return arr;
  37. }
  38. }

 

 

 03、插入排序 

插入排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了,因为只要打过扑克牌的人都应该能够秒懂。插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

插入排序和冒泡排序一样,也有一种优化算法,叫做拆半插入。

1)算法步骤

  • 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
  • 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

  1. public class InsertSort {
  2. /**
  3. * 插入排序:
  4. *
  5. * 1)算法步骤
  6. 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
  7. 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
  8. * @param args
  9. */
  10. public static void main(String[] args) {
  11. int[] sourceArray ={3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};
  12. sort(sourceArray);
  13. }
  14. private static int[] sort(int[] sourceArray) {
  15. //对数组进去拷贝, 不改变参数内容
  16. int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
  17. //从下标为1的元素开始选择合适的位置插入, 因为下标为0的只有一个元素, 默认是有序的
  18. for (int i = 1; i < arr.length; i++) {
  19. //记录要插入的数据
  20. int tmp = arr[i];
  21. //从已经排序的序列最右边的开始比较, 找到比其小的数
  22. int j = i;
  23. while (j > 0 && tmp < arr[j-1]) {
  24. arr[j] = arr[j-1];
  25. j--;
  26. }
  27. //存在比其小的数, 插入
  28. if (j != i) {
  29. arr[j] = tmp;
  30. }
  31. }
  32. System.out.println("插入排序: "+Arrays.toString(arr));
  33. return arr;
  34. }
  35. }

 

 

04、希尔排序 

希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位;

希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

1)算法步骤

  • 选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
  • 按增量序列个数 k,对序列进行 k 趟排序;
  • 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

  1. public class ShellSort {
  2. /**
  3. * 希尔排序:
  4. *
  5. 1)算法步骤
  6. 选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
  7. 按增量序列个数 k,对序列进行 k 趟排序;
  8. 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
  9. * @param args
  10. */
  11. public static void main(String[] args) {
  12. int[] sourceArray ={3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};
  13. sort(sourceArray);
  14. }
  15. private static int[] sort(int[] sourceArray) {
  16. //对数组进去拷贝, 不改变参数内容
  17. int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
  18. int gap = 1;
  19. while (gap < arr.length) {
  20. gap = gap * 3 + 1;
  21. }
  22. while (gap > 0) {
  23. for (int i = gap; i < arr.length; i++) {
  24. int tmp = arr[i];
  25. int j = i - gap;
  26. while (j >= 0 && arr[j] > tmp) {
  27. arr[j + gap] = arr[j];
  28. j -= gap;
  29. }
  30. arr[j + gap] = tmp;
  31. }
  32. gap = (int) Math.floor(gap / 3);
  33. }
  34. System.out.println("希尔排序: "+Arrays.toString(arr));
  35. return arr;
  36. }
  37. }

 

 

05、归并排序 

归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

作为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法:

  • 自上而下的递归(所有递归的方法都可以用迭代重写,所以就有了第 2 种方法);
  • 自下而上的迭代;

在《数据结构与算法 JavaScript 描述》中,作者给出了自下而上的迭代方法。但是对于递归法,作者却认为:

However, it is not possible to do so in JavaScript, as the recursion goes too deep for the language to handle.

然而,在 JavaScript 中这种方式不太可行,因为这个算法的递归深度对它来讲太深了。

说实话,我不太理解这句话。意思是 JavaScript 编译器内存太小,递归太深容易造成内存溢出吗?还望有大神能够指教。

和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是 O(nlogn) 的时间复杂度。代价是需要额外的内存空间。

1)算法步骤

  • 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
  • 设定两个指针,最初位置分别为两个已经排序序列的起始位置;
  • 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
  • 重复步骤 3 直到某一指针达到序列尾;
  • 将另一序列剩下的所有元素直接复制到合并序列尾。

  1. public class MergeSort {
  2. /**
  3. * 归并排序:
  4. 1)算法步骤
  5. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;设定两个指针,最初位置分别为两个已经排序序列的起始位置;
  6. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;重复步骤 3 直到某一指针达到序列尾;将另一序列剩下的所有元素直接复制到合并序列尾。
  7. * @param args
  8. */
  9. public static void main(String[] args) {
  10. int[] sourceArray ={3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};
  11. sort(sourceArray);
  12. }
  13. private static int[] sort(int[] sourceArray) {
  14. //对数组进去拷贝, 不改变参数内容
  15. int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
  16. if (arr.length < 2) {
  17. return arr;
  18. }
  19. int middle = (int) Math.floor(arr.length / 2);
  20. int[] left = Arrays.copyOfRange(arr,0,middle);
  21. int[] rigth = Arrays.copyOfRange(arr,middle,arr.length);
  22. return merge(sort(left),sort(rigth));
  23. }
  24. private static int[] merge(int[] left, int[] rigth) {
  25. int[] result = new int[left.length + rigth.length];
  26. int i = 0;
  27. while (left.length > 0 && rigth.length > 0) {
  28. if (left[0] <= rigth[0]) {
  29. result[i++] = left[0];
  30. left = Arrays.copyOfRange(left, 1, left.length);
  31. } else {
  32. result[i++] = rigth[0];
  33. rigth = Arrays.copyOfRange(rigth,1,rigth.length);
  34. }
  35. }
  36. while (left.length > 0) {
  37. result[i++] = left[0];
  38. left = Arrays.copyOfRange(left,1,left.length);
  39. }
  40. while (rigth.length > 0) {
  41. result[i++] = rigth[0];
  42. rigth = Arrays.copyOfRange(rigth,1,rigth.length);
  43. }
  44. System.out.println("归并排序: "+Arrays.toString(result));
  45. return result;
  46. }
  47. }

 

 

 06、快速排序 

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。

快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。

快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。 

快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高!它是处理大数据最快的排序算法之一了。虽然 Worst Case 的时间复杂度达到了 O(n²),但是人家就是优秀,在大多数情况下都比平均时间复杂度为 O(n logn) 的排序算法表现要更好,可是这是为什么呢,我也不知道。好在我的强迫症又犯了,查了 N 多资料终于在《算法艺术与信息学竞赛》上找到了满意的答案:

快速排序的最坏运行情况是 O(n²),比如说顺序数列的快排。但它的平摊期望时间是 O(nlogn),且 O(nlogn) 记号中隐含的常数因子很小,比复杂度稳定等于 O(nlogn) 的归并排序要小很多。所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。 

1)算法步骤

  • 从数列中挑出一个元素,称为 “基准”(pivot);
  • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

 递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

  1. public class QuickSort {
  2. /**
  3. * 快速排序:
  4. 1)算法步骤
  5. 从数列中挑出一个元素,称为 “基准”(pivot);重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。
  6. 这个称为分区(partition)操作;递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,
  7. 但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
  8. */
  9. public static void main(String[] args) {
  10. int[] sourceArray ={3,44,38,5,47,15,36,26,27,2,46,4,19,50,48};
  11. sort(sourceArray);
  12. }
  13. private static int[] sort(int[] sourceArray) {
  14. //对数组进去拷贝, 不改变参数内容
  15. int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
  16. return quickSort(arr, 0, arr.length - 1);
  17. }
  18. private static int[] quickSort(int[] arr, int left, int rigth) {
  19. if (left < rigth) {
  20. int partitionIndex = partition(arr,left,rigth);
  21. quickSort(arr,left,partitionIndex - 1);
  22. quickSort(arr,partitionIndex + 1, rigth);
  23. }
  24. System.out.println("快速排序: "+Arrays.toString(arr));
  25. return arr;
  26. }
  27. private static int partition(int[] arr, int left, int rigth) {
  28. //设定基准值(pivot)
  29. int pivot = left;
  30. int index = pivot + 1;
  31. for (int i = index; i <= rigth; i++) {
  32. if (arr[i] < arr[pivot]) {
  33. swap(arr,i,index);
  34. index++;
  35. }
  36. }
  37. swap(arr,pivot,index-1);
  38. return index-1;
  39. }
  40. private static void swap(int[] arr, int i, int j) {
  41. int temp = arr[i];
  42. arr[i] = arr[j];
  43. arr[j] = temp;
  44. }
  45. }

 

 

07、堆排序 

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法:

  • 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
  • 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

堆排序的平均时间复杂度为 Ο(nlogn)。

1)算法步骤

  • 创建一个堆 H[0……n-1];
  • 把堆首(最大值)和堆尾互换;
  • 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
  • 重复步骤 2,直到堆的尺寸为 1。

  1. public class HeapSort {
  2. /**
  3. * 堆排序: 算法步骤 创建一个堆 H[0……n-1];把堆首(最大值)和堆尾互换;把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;重复步骤 2,直到堆的尺寸为 1。
  4. */
  5. public static void main(String[] args) {
  6. int[] sourceArray ={91,60,96,13,35,65,46,65,10,30,20,31,77,81,22};
  7. sort(sourceArray);
  8. }
  9. private static int[] sort(int[] sourceArray) {
  10. //对数组进去拷贝, 不改变参数内容
  11. int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
  12. int len = arr.length;
  13. buildMaxHeap(arr,len);
  14. for (int i = len-1; i > 0; i--) {
  15. swap(arr,0,i);
  16. len--;
  17. heapify(arr,0,len);
  18. }
  19. System.out.println("堆排序: "+Arrays.toString(arr));
  20. return arr;
  21. }
  22. private static void buildMaxHeap(int[] arr, int len) {
  23. for (int i = (int) Math.floor(len/2); i >= 0; i--) {
  24. heapify(arr,i,len);
  25. }
  26. }
  27. private static void heapify(int[] arr, int i, int len) {
  28. int left = 2*i+1;
  29. int rigth = 2*i+2;
  30. int largest = i;
  31. if (left < len && arr[left] > arr[largest]) {
  32. largest = left;
  33. }
  34. if (rigth < len && arr[rigth] > arr[largest]) {
  35. largest = left;
  36. }
  37. if (largest != i) {
  38. swap(arr,i,largest);
  39. heapify(arr,largest,len);
  40. }
  41. }
  42. private static void swap(int[] arr, int i, int j) {
  43. int temp = arr[i];
  44. arr[i] = arr[j];
  45. arr[j] = temp;
  46. }
  47. }

 

 

08、计数排序 

 计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。

  1. public class CountingSort {
  2. /**
  3. * 计数排序:
  4. 计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
  5. * @param args
  6. */
  7. public static void main(String[] args) {
  8. int[] sourceArray ={2,3,8,7,1,2,2,2,7,3,9,8,2,1,4,2,4,6,9,2};
  9. sort(sourceArray);
  10. }
  11. private static int[] sort(int[] sourceArray) {
  12. //对数组进去拷贝, 不改变参数内容
  13. int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
  14. int maxValue = getMaxValue(arr);
  15. return countingSrot(arr,maxValue);
  16. }
  17. private static int[] countingSrot(int[] arr, int maxValue) {
  18. int bucketLen = maxValue + 1;
  19. int[] bucket = new int[bucketLen];
  20. for (int value : arr) {
  21. bucket[value]++;
  22. }
  23. int sortedIndex = 0;
  24. for (int j = 0; j < bucketLen; j++) {
  25. while (bucket[j] > 0) {
  26. arr[sortedIndex++] = j;
  27. bucket[j]--;
  28. }
  29. }
  30. System.out.println("计数排序: "+Arrays.toString(arr));
  31. return arr;
  32. }
  33. private static int getMaxValue(int[] arr) {
  34. int maxValue = arr[0];
  35. for (int value : arr) {
  36. if (maxValue < value) {
  37. maxValue = value;
  38. }
  39. }
  40. return maxValue;
  41. }
  42. }

 

 

09、桶排序 

桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点:

  1. 在额外空间充足的情况下,尽量增大桶的数量
  2. 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中 

同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。

1)什么时候最快

当输入的数据可以均匀的分配到每一个桶中。

2)什么时候最慢

当输入的数据被分配到了同一个桶中。

  1. public class BucketSort {
  2. /**
  3. * 桶排序
  4. */
  5. private static final InsertSort insertSort = new InsertSort();
  6. public int[] sort(int[] sourceArray) throws Exception {
  7. // 对 arr 进行拷贝,不改变参数内容
  8. int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
  9. return bucketSort(arr, 5);
  10. }
  11. private int[] bucketSort(int[] arr, int bucketSize) throws Exception {
  12. if (arr.length == 0) {
  13. return arr;
  14. }
  15. int minValue = arr[0];
  16. int maxValue = arr[0];
  17. for (int value : arr) {
  18. if (value < minValue) {
  19. minValue = value;
  20. } else if (value > maxValue) {
  21. maxValue = value;
  22. }
  23. }
  24. int bucketCount = (int) Math.floor((maxValue - minValue) / bucketSize) + 1;
  25. int[][] buckets = new int[bucketCount][0];
  26. // 利用映射函数将数据分配到各个桶中
  27. for (int i = 0; i < arr.length; i++) {
  28. int index = (int) Math.floor((arr[i] - minValue) / bucketSize);
  29. buckets[index] = arrAppend(buckets[index], arr[i]);
  30. }
  31. int arrIndex = 0;
  32. for (int[] bucket : buckets) {
  33. if (bucket.length <= 0) {
  34. continue;
  35. }
  36. // 对每个桶进行排序,这里使用了插入排序
  37. bucket = insertSort.sort(bucket);
  38. for (int value : bucket) {
  39. arr[arrIndex++] = value;
  40. }
  41. }
  42. return arr;
  43. }
  44. //自动扩容,并保存数据
  45. private int[] arrAppend(int[] arr, int value) {
  46. arr = Arrays.copyOf(arr, arr.length + 1);
  47. arr[arr.length - 1] = value;
  48. return arr;
  49. }
  50. }

 

 

10、基数排序 

基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。

1)基数排序 vs 计数排序 vs 桶排序

基数排序有两种方法:

这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异:

  • 基数排序:根据键值的每位数字来分配桶;
  • 计数排序:每个桶只存储单一键值;
  • 桶排序:每个桶存储一定范围的数值; 

  1. public class RadixSort {
  2. /**
  3. * 基数排序
  4. * @param sourceArray
  5. * @return
  6. * @throws Exception
  7. */
  8. public int[] sort(int[] sourceArray) throws Exception {
  9. // 对 arr 进行拷贝,不改变参数内容
  10. int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
  11. int maxDigit = getMaxDigit(arr);
  12. return radixSort(arr, maxDigit);
  13. }
  14. //获取最高位数
  15. private int getMaxDigit(int[] arr) {
  16. int maxValue = getMaxValue(arr);
  17. return getNumLenght(maxValue);
  18. }
  19. private int getMaxValue(int[] arr) {
  20. int maxValue = arr[0];
  21. for (int value : arr) {
  22. if (maxValue < value) {
  23. maxValue = value;
  24. }
  25. }
  26. return maxValue;
  27. }
  28. protected int getNumLenght(long num) {
  29. if (num == 0) {
  30. return 1;
  31. }
  32. int lenght = 0;
  33. for (long temp = num; temp != 0; temp /= 10) {
  34. lenght++;
  35. }
  36. return lenght;
  37. }
  38. private int[] radixSort(int[] arr, int maxDigit) {
  39. int mod = 10;
  40. int dev = 1;
  41. for (int i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
  42. // 考虑负数的情况,这里扩展一倍队列数,其中 [0-9]对应负数,[10-19]对应正数 (bucket + 10)
  43. int[][] counter = new int[mod * 2][0];
  44. for (int j = 0; j < arr.length; j++) {
  45. int bucket = ((arr[j] % mod) / dev) + mod;
  46. counter[bucket] = arrayAppend(counter[bucket], arr[j]);
  47. }
  48. int pos = 0;
  49. for (int[] bucket : counter) {
  50. for (int value : bucket) {
  51. arr[pos++] = value;
  52. }
  53. }
  54. }
  55. return arr;
  56. }
  57. //自动扩容,并保存数据
  58. private int[] arrayAppend(int[] arr, int value) {
  59. arr = Arrays.copyOf(arr, arr.length + 1);
  60. arr[arr.length - 1] = value;
  61. return arr;
  62. }
  63. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/462167
推荐阅读
相关标签
  

闽ICP备14008679号