当前位置: 首页 > article >正文

Java-数据结构-排序(三) |ू・ω・` )

目录

❄️一、归并排序:

              ☞ 基本思想:

             ☞ 代码:

             ☞ 归并排序的非递归方法:

❄️二、排序算法的分析:

 ❄️三、非基于比较的排序:

 ❄️总结:


❄️一、归并排序:

              ☞ 基本思想:

 归并排序是一个建立在归并操作上的一种排序算法,这个算法是采用分治法的典型案例。

 就是:将有序的子序列合并,得到一个完全有序的列表。

       即是先使每个子序列有序,再使子序列段间有序。

   若将两个有序的列表合并成一个列表,称为二路归并。

 我们来看看这个排序是如何进行排序的:

     就是分成了 分解操作 和 合并操作 ,那么我们的这个 分解操作是如何做的,合并操作又是如何做的?我们呢来一一的看看如何操作的:

分解操作:

   1、我们呢对于这个数据 开始位置设置为left ,结束位置设置为 right ,之后求出中间值 mid ,

   2、之后把left 到 mid 下标的值分为一组,mid +1 到 right 的值分为一组。

   3、对于 left 到 mid这一组,再分的话把 mid 的下标给到 right 之后再求 mid 位置。

   4、对于 mid+1 到 right 这一组,再分的话就把 mid+1 的下标给到 left 之后再求 mid 位置。

   5、这样直至我们的 left >= right 的时候结束。

OK,我们的分解思路就是这样的,我们呢来看看代码是如何编写的 :

public static void mergeSort(int[] array) {
        mergeSortTmp(array,0,array.length - 1);
    }

    private static void mergeSortTmp(int[] array, int left, int right) {
        if (left >= right) {
            return;
        }
        //分解操作
        int mid = (left + right) / 2;
        mergeSortTmp(array,left,mid);
        mergeSortTmp(array,mid+1,right);
        
    }

  合并操作:

1、我们在每次分解完一个区间的数据之后呢,就对其进行合并操作,合并之后就是有序的数据了

2、比如我们合并长度为2 的两个有序的数据,就是相当于合并两个有序的数组

3、我们要知道每个数组的开始和结尾

 第一个数组:开头是left 用s1 保存,结尾是mid 用e1 保存

 第二个数组:开头是mid+1 用s2 保存,结尾是right 用e2 保存

4、我们把 s1 和 s2 下标的值进行比较之后小的值呢放到新的数组中,只要有一个超过了e1或者e2就需要退出循环,进行下一步的判断操作。

5、还有判断哪个数组放到新的数组中,之后再把没放入的放进数组中

6、把排好序的数组放到原先的数组中,但是要注意放的时候我们的原数组的下标要加上一个left

     因为我们第二次排序的时候呢下标不是从 0 开始的是从 left 下标开始的。

我们来看看代码是如何编写的: 


    private static void merge(int[] array, int left, int mid, int right) {
        int[] tmp = new int[right - left + 1];
        int k = 0;
        int s1 = left;
        int e1 = mid;
        int s2 = mid + 1;
        int e2 = right;

        while(s1 <= e1 && s2 <= e2) {

            if (array[s1] <= array[s2]) {
                tmp[k++] = array[s1++];
            }else {
                tmp[k++] = array[s2++];
            }
        }

        while (s1 <= e1) {
            tmp[k++] = array[s1++];
        }
        while (s2 <= e2) {
            tmp[k++] = array[s2++];
        }

        for (int i = 0; i < k; i++) {
            array[i+left] = tmp[i];
        }
    }

这就是我们的合并排序的代码了,我们来看看整体的代码:


             ☞ 代码:

 /**
     * 归并排序
     * 时间复杂度:O(N*logN)
     * 空间复杂度:O(N)
     * 稳定性:稳定
     * @param array
     */
    public static void mergeSort(int[] array) {
        mergeSortTmp(array,0,array.length - 1);
    }

    private static void mergeSortTmp(int[] array, int left, int right) {
        if (left >= right) {
            return;
        }
        //分解操作
        int mid = (left + right) / 2;
        mergeSortTmp(array,left,mid);
        mergeSortTmp(array,mid+1,right);
        //每一个分解完之后呢,都要进行排序合并操作
        merge(array,left,mid,right);
    }

    private static void merge(int[] array, int left, int mid, int right) {
        int[] tmp = new int[right - left + 1];
        int k = 0;
        int s1 = left;
        int e1 = mid;
        int s2 = mid + 1;
        int e2 = right;

        while(s1 <= e1 && s2 <= e2) {

            if (array[s1] <= array[s2]) {
                tmp[k++] = array[s1++];
            }else {
                tmp[k++] = array[s2++];
            }
        }

        while (s1 <= e1) {
            tmp[k++] = array[s1++];
        }
        while (s2 <= e2) {
            tmp[k++] = array[s2++];
        }

        for (int i = 0; i < k; i++) {
            array[i+left] = tmp[i];
        }
    }

             ☞ 归并排序的非递归方法:

  我们的非递归呢,就是把我们的分解操作变成非递归的操作,那么如何做呢?

  对于分解操作,我们一开始是不是可以看成它们是 一个一个有序,之后是两个两个有序,之后是四个四个有序,这样下去就可以把我们的分解操实现了。

1、我们定义一个 gap 用来 判断是几个一组的

      我们的 gap 每次变化都是 乘2 的,并且 gap < 数组长度 才可以执行下面的操作

2、我们的定义个 i 从 0 下标开始,我们的 left 就是 i 

    这里我们的 i 不能是++操作进行往下走,我们的 i 这里是 i = i + gap*2

3、mid = left + gap - 1

     在赋值之后呢,我们的mid 要进行判断操作,是否是 >= 数组的长度 ,如果是,就把其进行 - 1

     因为这里我们之后要传给 合并有序数组的操作,防止数组越界

4、right = mid + gap

    这里同样要进行判断操作,是否是 >= 数组的长度 ,如果是,就把其进行 - 1

 

 代码:

public static void mergeSort(int[] array) {
        mergeSortNor(array);
    }

private static void mergeSortTmp(int[] array, int left, int right) {
        if (left >= right) {
            return;
        }
        //分解操作
        int mid = (left + right) / 2;
        mergeSortTmp(array,left,mid);
        mergeSortTmp(array,mid+1,right);
        //每一个分解完之后呢,都要进行排序合并操作
        merge(array,left,mid,right);
    }
//非递归实现归并
public static void mergeSortNor(int[] array) {
        int gap = 1;
        while (gap < array.length) {
            for (int i = 0; i < array.length; i = i + gap*2) {
                int left = i;
                int mid = left + gap - 1;
                if (mid >= array.length) {
                    mid = array.length - 1;
                }
                int right = mid + gap;
                if (right >= array.length) {
                    right = array.length - 1;
                }
                merge(array,left,mid,right);
            }
            gap *= 2;
        }
    }


❄️二、排序算法的分析:

排序方法最好平均最坏(时间复杂度)空间复杂度稳定性
冒泡排序O(N^2)O(N^2)O(N^2)O(1)稳定
插入排序O(N)O(N^2)O(N^2)O(1)稳定
选择排序O(N^2)O(N^2)O(N^2)O(1)不稳定
希尔排序O(N)O(N^1.3)O(N^1.5)O(1)不稳定
堆排序O(N*logN)O(N*logN)O(N*logN)O(1)不稳定
快速排序O(N*logN)O(N*logN)O(N^2)O(log(N))~O(N)不稳定
归并排序O(N*logN)O(N*logN)O(N*logN)O(N)稳定

 ❄️三、非基于比较的排序:

     我们之前介绍的排序都是需要比较的排序方法,我们还有不需要比较的排序方法:计数排序、基数排序、桶排序。

我们的非基于比较的排序可以通过 “传送门” 来了解:

基数排序:

              基数排序

桶排序:

              桶排序

当然如果需要 计数排序的 传送门 的话,这里也有:

              计数排序

 


 ❄️总结:

      OK,到这里呢我们的排序呢就结束了,这次关于排序的代码呢还是比较多的,要认真的理解它们,同时要画图的去理解它们的执行流程。 下一篇博客呢我们就要介绍关于Map与Set的知识啦,让我们尽情期待吧!!!拜拜~~~


http://www.kler.cn/a/318663.html

相关文章:

  • DIP switch是什么?
  • 手动实现promise的all,race,finally方法
  • 群控系统服务端开发模式-应用开发-前端个人信息功能
  • AI大模型开发架构设计(18)——基于大模型构建企业知识库案例实战
  • 从社交媒体到元宇宙:Facebook未来发展新方向
  • java八股-jvm入门-程序计数器,堆,元空间,虚拟机栈,本地方法栈,类加载器,双亲委派,类加载执行过程
  • 【网络安全】密码学的新进展
  • Nginx 如何开启压缩
  • 伊犁云计算22-1 rhel8 dhcp 配置
  • YOLOv10改进,YOLOv10主干网络替换为VanillaNet( CVPR 2023 华为提出的全新轻量化架构),大幅度涨点
  • 操作系统知识3
  • 华为全联接大会HUAWEI Connect 2024印象(一):OpenEuler
  • uniapp沉浸式导航栏+自定义导航栏组件
  • 深入理解端口、端口号及FTP的基本工作原理
  • CREO教程——2 绘制标准图纸
  • python/requests库的使用/爬虫基础工具/
  • 最新版C/C++通过CLion2024进行Linux远程开发保姆级教学
  • 【Docker】基于docker compose部署artifactory-cpp-ce服务
  • 【车联网安全】车端知识调研
  • 产品经理面试整理-软件产品经理的常用工具
  • SpringBoot框架在文档管理中的创新应用
  • 系统架构笔记-3-信息系统基础知识
  • 探讨MySQL中的GROUP BY语句大小写敏感性
  • SegFormer网络结构的学习和重构
  • CSP-S 2024 提高级 第一轮(初赛) 阅读程序(2)
  • 【OSS安全最佳实践】降低因操作失误等原因导致数据丢失的风险