八大排序图解算法

Bertha 。 2023-02-12 14:24 141阅读 0赞

其实八大排序如果弄清楚它们的原理并不难,虽然里面有几种排序写起来也很麻烦。
但是最难的往往就是,我们会把它们相互混淆,我给每个排序画了一张动图,看图记忆就好很多了。

每种排序都有相对应的解释和图,大家可以看完解释和图然后按照自己的思路去写代码。(当然也提供了测试好的代码)

末尾提供了测试代码,你只需要改动一行代码,就可以测试你的排序是否正确,如果错误会打印出原本序列你的排序正确的排序,以帮助你更好的排错。

稳定性:如果一组待排序的数字中,有两个相同的数字。在完成排序后,这两个数字的相对位置不变,即该排序是稳定排序。

不稳定的排序算法:堆排序、快速排序、希尔排序、直接选择排序.

0(nlogn) 快速排序、堆排序、归并排序,快速排序最好

初始顺序对排序没有影响的是 堆排序

在这里插入图片描述

一、冒泡排序






















平均时间复杂度 最好时间复杂度 最坏时间复杂度 空间复杂度 稳定性 类别
O(n2) O(n) O(n2) O(1) 稳定 交换排序

1-1:冒泡排序的解释

循环遍历 0-length-1 的元素,找到最合适每个位置的元素。
把位置 i 的元素和i后面的的每个元素对比,找到最小的元素放在 i 这个位置。(找最大还是最小取决你怎么排序)
下面是图示:

在这里插入图片描述

1-2:冒泡排序代码

  1. public void bubbleSort(int[] arr){
  2. int tmp;
  3. for (int i = 0;i < arr.length - 1; i++){
  4. for (int j = i+1; j < arr.length; j++){
  5. if (arr[i] > arr[j]){
  6. tmp = arr[j];
  7. arr[j] = arr[i];
  8. arr[i] = tmp;
  9. }
  10. }
  11. }
  12. }

二、快速排序






















平均时间复杂度 最好时间复杂度 最坏时间复杂度 空间复杂度 稳定性 类别
O(nlog2n) O(nlog2n) O(n2) O(nlog2n) 不稳定 交换排序

2-1:快速排序的解释

每次选择一个数作为标准,然后比它大的放在右边,比它小的放在左边。
通过上面的操作把数组分成了两份,然后每份继续重复这样的操作,直到排序好。

注:
1、为了方便,这个标准数,我们每次取第一个数
2、当左边长度是1的时候就是排序好了。(同理右边也是)

在这里插入图片描述

2-2:快速排序代码

  1. public static void quickSort(int[] arr,int left, int right){
  2. int tmpLeft = left;
  3. int tmpRight = right;
  4. int cur = arr[left];
  5. while (left < right){
  6. while (arr[right] >= cur && right > left) {
  7. right--;
  8. }
  9. arr[left] = arr[right];
  10. while (arr[left] < cur && right > left) {
  11. left ++;
  12. }
  13. arr[right] = arr[left];
  14. }
  15. arr[left] = cur;
  16. if (left - tmpLeft > 1){
  17. quickSort(arr,tmpLeft,left-1);
  18. }
  19. if (tmpRight - left > 0){
  20. quickSort(arr,left+1,tmpRight);
  21. }
  22. }

三、直接插入排序






















平均时间复杂度 最好时间复杂度 最坏时间复杂度 空间复杂度 稳定性 类别
O(n2) O(n) O(n2) O(1) 稳定 插入排序

3-1:直接插入排序的解释

我们从 1 开始遍历每一个元素(i),然后把元素 i 插入到 0-i 里面最合适的位置,这样就排好序了。

在这里插入图片描述

3-2:直接插入排序的代码

  1. public void insertSort(int[] arr){
  2. int tmp,j;
  3. for (int i = 1;i < arr.length; i++){
  4. if (arr[i] < arr[i-1]){
  5. tmp = arr[i];
  6. for (j = i;j > 0; j--){
  7. if (tmp < arr[j-1]){
  8. arr[j] = arr[j-1];
  9. }else {
  10. break;
  11. }
  12. }
  13. arr[j] = tmp;
  14. }
  15. }
  16. }

四、希尔排序

4-1:希尔排序解释






















平均时间复杂度 最好时间复杂度 最坏时间复杂度 空间复杂度 稳定性 类别
O(n1.3) O(n) O(n2) O(1) 不稳定 交换排序

希尔排序其实是直接插入排序的一个改进。 直接插入排序每次移动的步长都是 1 ,而希尔排序的步长从 size 开始 直到 1,这样做的使得 大的数字比较靠后,小的数字比较靠前。 当进行步长为 1 的直接插入排序的时候,就不会出现每次移动很多的情况。

先将整个待排序列分割成若干个子序列,对每个子序列进行直接插入排序,等到整个序列基本有序的时候,在进行一次整体的直接插入排序(也就是 步长为 1)。

在这里插入图片描述

  1. public void shellInsert(int[] arr, int step){
  2. int tmp;
  3. int m;
  4. for (int i = 0; i < step; i++){
  5. for (int n = i + step;n < arr.length; n += step){
  6. if (arr[n] < arr[n - step]){
  7. tmp = arr[n];
  8. for (m = n-step;m >= 0; m -= step){
  9. if (tmp < arr[m]){
  10. arr[m + step] = arr[m];
  11. }else {
  12. break;
  13. }
  14. }
  15. arr[m+step] = tmp;
  16. }
  17. }
  18. }
  19. }
  20. public void shellSort(int[] arr){
  21. int step = arr.length / 2;
  22. // int step = 1;
  23. while (step >= 1){
  24. shellInsert(arr, step);
  25. step = step / 2;
  26. }
  27. }

五、直接选择排序

5-1:直接选择排序解释






















平均时间复杂度 最好时间复杂度 最坏时间复杂度 空间复杂度 稳定性 类别
O(n2) O(n2) O(n2) O(1) 不稳定 选择排序

每次从i - length-1中选择出 一个最小(最大)的数字,然后和 i 进行交换。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i4WCqdFO-1590475243176)(在这里插入图片描述

5-2:直接选择排序代码

  1. public void selectSort(int[] arr){
  2. int min_i,tmp;
  3. for (int i = 0;i < arr.length - 1; i++){
  4. min_i = i;
  5. for (int j = i+1;j < arr.length; j++){
  6. if (arr[min_i] > arr[j]){
  7. min_i = j;
  8. }
  9. }
  10. tmp = arr[i];
  11. arr[i] = arr[min_i];
  12. arr[min_i] = tmp;
  13. }
  14. }

六、堆排序

6-1:堆排序解释






















平均时间复杂度 最好时间复杂度 最坏时间复杂度 空间复杂度 稳定性 类别
O(nlog2n) O(nlog2n) O(nlog2n) O(1) 不稳定 选择排序

把待排序列变成最大堆(最小堆),然后取出对顶元素。重复以上操作,直到排序结束。
最大堆你可以把它看成二叉树,根元素,大于左右节点。

在这里插入图片描述

6-2:堆排序代码

  1. //当列表第一个是以下标0开始,结点下标为i,左孩子则为2*i+1,右孩子下标则为2*i+2,若下标以1开始,左孩子则为2*i,右孩子则为2*i+1
  2. public static void heapAdjust(int a[], int s, int m){
  3. int key = a[s];
  4. for(int j = 2*s + 1; j <= m; j = 2*j + 1 ){
  5. // 找到左节点和右节点中小的节点
  6. if(j < m && a[j] <= a[j+1] ) {
  7. ++j;
  8. }
  9. // 如果当前值比左右节点最小值还小就不用管了
  10. if( a[j] <= key ){
  11. break;
  12. }
  13. a[s] = a[j];
  14. s = j;
  15. }
  16. a[s] = key;
  17. }
  18. public static void heap_sort(int a[], int size){
  19. //初始建堆,从最后一个非叶子节点开始
  20. for(int i = size/2-1; i >= 0; --i){
  21. heapAdjust(a, i, size-1);
  22. }
  23. //取堆顶,并且调整
  24. int tmp;
  25. for(int i = size-1; i > 0 ; --i){
  26. tmp = a[0];
  27. a[0] = a[i];
  28. a[i] = tmp;
  29. heapAdjust(a, 0, i-1);
  30. }
  31. }

七、归并排序

7-1:归并排序的解释




















平均时间复杂度 最好时间复杂度 最坏时间复杂度 空间复杂度 稳定性
O(nlog2n) O(nlog2n) O(nlog2n) O(n) 稳定

把序列尽可能的等分,然后两半分别排序。排序好后再合并排序

在这里插入图片描述

7-2:归并排序代码

  1. /** * 每个部分进行排序 */
  2. public void sort(int[] arr,int i,int mid,int j){
  3. int[] brr = new int[j-i+1];
  4. int start_one = i;
  5. int start_two = mid+1;
  6. int cur = 0;
  7. while (start_one <= mid && start_two <= j){
  8. brr[cur ++] = arr[start_one] < arr[start_two] ? arr[start_one ++] : arr[start_two ++];
  9. }
  10. while (start_one <= mid){
  11. brr[cur ++] = arr[start_one++];
  12. }
  13. while (start_two <= j){
  14. brr[cur ++] = arr[start_two++];
  15. }
  16. for (int k = 0;k < cur; k++){
  17. arr[i ++] = brr[k];
  18. }
  19. }
  20. /** * 拆分成一个个部分 */
  21. public void mergeSort(int[] arr,int i,int j){
  22. if (i >= j) return;
  23. int mid = (i + j) / 2;
  24. mergeSort(arr, i, mid);
  25. mergeSort(arr,mid + 1, j);
  26. sort(arr, i,mid,j);
  27. }

八、基数排序

8-1:基数排序的解释




















平均时间复杂度 最好时间复杂度 最坏时间复杂度 空间复杂度 稳定性
O(d(r+n)) O(d(n+rd)) O(nlog2n) O(n) 稳定

基数排序要根据具体的序列规则来做,这里以最简单的十位数为例,作图。
先以个位数进行排序,然后再以10位数进行排序。回收的结果就是排序好的结果。

在这里插入图片描述

8-2:基数排序代码

因为基数排序和具体的序列有关,这里以最简单的个位数为例,方便你阅读代码。

  1. public static void radixSort(int[] arr){
  2. // 创建0-9的桶
  3. List<List<Integer>> lists = new ArrayList<>();
  4. for (int i = 0;i < 10; i++){
  5. lists.add(new ArrayList<>());
  6. }
  7. // 按照顺序把数字装入桶中
  8. for (int j = 0;j < arr.length; j++){
  9. lists.get(arr[j]).add(arr[j]);
  10. }
  11. // 按照顺序回收桶中的数据
  12. int cur = 0;
  13. for (List<Integer> item : lists){
  14. for (Integer i : item){
  15. arr[cur ++] = i;
  16. }
  17. }
  18. }

测试代码

你可以使用下面的工具类,对你写的排序方法进行测试。

  1. /** * 测试排序规则 * 1、测试100次,每次随机出现0-100的数字 * 2、arr 使用系统的排序, brr我们自己的排序, crr 不排序 * 3、对比arr 和 brr看是否相同,如不相同就打印 arr、brr 、crr * 4、你每次只需要替换下面注释地 * * @author 小道仙 * @date 2020年5月23日 */
  2. public static boolean testSort(){
  3. int arrayLenght = 100;
  4. int[] arr = new int[arrayLenght];
  5. int[] brr = new int[arrayLenght];
  6. int[] crr = new int[arrayLenght];
  7. int tmp,cur;
  8. for (int i = 0;i < 10000; i++){
  9. cur = 0;
  10. for (int j = 0; j < arrayLenght; j++){
  11. tmp = (int)(Math.random()*100);
  12. arr[cur] = tmp;
  13. crr[cur] = tmp;
  14. brr[cur ++] = tmp;
  15. }
  16. Arrays.sort(arr);
  17. try {
  18. // 每次测试替换掉下面的这个排序方法
  19. // heap_sort(brr, brr.length);
  20. }catch (Exception e){
  21. e.printStackTrace();
  22. }
  23. for (int k = 0;k < cur; k++){
  24. if (arr[k] != brr[k]){
  25. print(arr,brr,crr);
  26. return false;
  27. }
  28. }
  29. }
  30. return true;
  31. }
  32. private static void print(int[] arr,int[] brr,int[] crr){
  33. System.out.print("arr : ");
  34. for (int i = 0;i < arr.length; i++){
  35. System.out.print(arr[i] + " ");
  36. }
  37. System.out.println();
  38. System.out.print("brr : ");
  39. for (int i = 0;i < brr.length; i++){
  40. System.out.print(brr[i] + " ");
  41. }
  42. System.out.println();
  43. System.out.print("crr : ");
  44. for (int i = 0;i < crr.length; i++){
  45. System.out.print(crr[i] + " ");
  46. }
  47. System.out.println();
  48. System.out.println();
  49. }

关注我吧,一个喜欢胡思乱想的程序员。

在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,141人围观)

还没有评论,来说两句吧...

相关阅读

    相关 排序图解算法

    > 其实八大排序如果弄清楚它们的原理并不难,虽然里面有几种排序写起来也很麻烦。 > 但是最难的往往就是,我们会把它们相互混淆,我给每个排序画了一张动图,看图记忆就好很多了。

    相关 排序算法

     概述排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。 我们这里说说

    相关 排序算法

    阅读此文推荐:[前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。][Link 1] 概述 排序有内部排序和外部排序,内部排序是数据

    相关 排序算法排序

    八大排序算法 1.冒泡排序 冒泡排序是一种交换排序, 就是两两比较待排序的元素, 若次序不满足要求则交换, 知道整个数组有序 基本思想 : 每次找到最大或最小值,

    相关 排序算法

    概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。 我们这里说说八