数据结构-8.Java. 七大排序算法(上篇)

news2024/11/23 19:11:39

本篇博客给大家带来的是排序的知识点, 由于时间有限, 分两天来写, 上篇主要实现 前四种排序算法: 直接插入, 希尔, 选择, 堆排。

文章专栏: Java-数据结构

若有问题 评论区见

欢迎大家点赞 评论 收藏 分享

如果你不知道分享给谁,那就分享给薯条.

你们的支持是我不断创作的动力 .

    

目录

    

  王子公主请阅

1.排序的概念及应用

1.1 排序的概念

1.3 常见的排序算法

2. 常见排序算法的实现(默认排升序)

2.1 插入排序

2.1.1基本思想:

2.1.2 直接插入排序

直接插入排序具体操作: 

2.1.3 希尔排序(缩小增量排序)

2.2 选择排序

2.2.1基本思想:

2.2.3 堆排序


  王子公主请阅

1.排序的概念及应用

1.1 排序的概念

排序:所谓排序,就是使一串记录按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

稳定性 :假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j] ,且 r[i] r[j] 之前,而在排序后的序列中, r[i] 仍在 r[j] 之前,则称这种排序算法是稳定的;否则称为不稳定的。
内部排序 :数据元素全部放在内存中的排序。
外部排序 :数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

1.3 常见的排序算法

2. 常见排序算法的实现(默认排升序)

2.1 插入排序

2.1.1基本思想:

直接插入排序是一种简单的插入排序法,其基本思想是:

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到 一个新的有序序列 。实际中我们玩扑克牌时,就用了插入排序的思想。

如下模拟动图: 

2.1.2 直接插入排序

当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,
此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置
array[i]插入,原来位置上的元素顺序后移
如下图: 

直接插入排序具体操作: 

第一步 理清思路:

 当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,

此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置
array[i]插入,原来位置上的元素顺序后移
第二步具体步骤: 

1.   定义 tmp 保存 排序前 i 下标对应的值.  for(i) 为 外循环, for(j) 为  内循环 , j = i -1 .

2.   遍历数组,  在内循环中,  tmp 与  array[ j ] 进行比较,, 若是 tmp 小 则  [ j + 1] = [ j ];

若是 tmp 大 则 直接 break;  

3.    在外循环中  将 最后 j + 1 位置的值 赋为 tmp值 , [ j + 1 ] = tmp;  

第三步  画图理解 , 一目了然 :

第四步  代码实现: 

 /**
     * 时间复杂度:
     *            最好情况:数据完全有序的时候 1 2 3 4 5 :O(N)
     *            最坏情况:数据完全逆序的时候 5 4 3 2 1 :O(N^2)
     *         结论: 数据越有序,排序越快, 
     *         场景: 现在有一组基本有序的数据,那么用哪个排序好点? - 直接插入排序.
     * 空间复杂度: O(1)
     * 稳定性:  稳定的排序
     *      一个本身就是稳定的排序 是可以实现为不稳定的排序的
     *      相反 一个本身就不稳定的排序  是不可以实现为稳定的排序的.
     *
     * @param //直接插入排序
     */
    public static void insertSort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            int tmp = array[i];
            int j = i-1;
            for (; j >= 0; j--) {
                if(array[j] > tmp) {  // >= 会变成不稳定的
                    array[j+1] = array[j];
                }else {
                    //array[j+1] = tmp; 执行了一次
                    break;
                }
            }
            //当j=-1时,a[j+1]=tmp这条语句没被执行,所以再写一次
            array[j+1] = tmp; //执行了两次, 故把第一次屏蔽.
        }
    }

最后总结: 

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1),它是一种稳定的排序算法
4. 稳定性:稳定 

2.1.3 希尔排序(缩小增量排序)

希尔排序本质上 就是 在对直接插入排序做优化。

第一步 了解基本思想: 

先选定一个整数 gap ,把待排序文件中所有记录分成多个组,
此处 gap 通常都是 初始化成 gap = array.length/2;
所有 距离为 gap 的记录 在同一组内,并对每一组内的记录进行直接插入排序。
每进行一次直接插入排序 gap /= 2 重复上述分组和排序的工作。当 gap =  时,所有记录在同一组内进行直接插入排序
第二步 画图辅助理解: 
第三步 具体步骤:
具体步骤与 直接插入排序的大体相同, 将 外循环条件换成for (int i = gap; i < array.length; i++),
外循环中 j = i - gap 而不是 - 1;  
同时, 内循环变成for (; j >= 0; j -= gap)
赋值条件变为array[j+gap] = tmp;
最后在直接插入排序外加一层 gap /= 2 的循环 就大功告成了.
第四步  代码实现: 
/**
     * 希尔排序
     *
     * 时间复杂度:
     *        n^1.3 - n^1.5
     *  空间复杂度: O(1)
     *
     *  稳定性: 不稳定
     * @param //shell
     */
    public static void shellSort(int[] array) {
        int gap = array.length;
        while(gap > 1) {
            gap /= 2;
            shell(array,gap);
        }
        //gap /= 2;不用写   因为gap=2或3时, gap/2 = 1.已经进行了直接插入排序.
    }
    private static void shell(int[] array,int gap) {
        for (int i = gap; i < array.length; i++) {
            int tmp = array[i];
            int j = i-gap;
            for (; j >= 0; j -= gap) {
                if(array[j] > tmp) {
                    array[j+gap] = array[j];
                }else {
                    break;
                }
            }
            array[j+gap] = tmp;
        }
    }

最后 希尔排序的特性总结

1. 希尔排序是对直接插入排序的优化。
2. gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。
3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定:O(n^1.25) ~ O(1.6 * n^1.25)
4. 稳定性:不稳定

2.2 选择排序

2.2.1基本思想:

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
如下模拟动图: 
第一步 理清思路:
在元素集合 array[ i ] ~ array[ n-1 ] 中选择关键码最 的数据元素
若它不是这组元素中的 第一个 元素,则将它与这组元素中的最后一个(第一个)元素交换
在剩余的 array[ i ]  ~ array[ n-2 ] array[ i+1 ]  ~  array[ n-1 ] )集合中,重复上述步骤,直到集合剩余 1 个元素
第二步 具体步骤: 
1.  写个双层循环, for( i ) 为 外层循环,  for( j ) 为外层循环, 定义minIndex =  i , j = i + 1;
2.   [minIndex] 与 [ j ] 比较, 当[minIndex] > [ j ] 时  二者交换. 
第三步  代码实现:
/**
     * 选择排序
     *
     * 时间复杂度:
     *      最好: O(N^2)
     *      最坏: O(N^2)
     *空间复杂度:O(N)
     *
     * 稳定性: 不稳定
     *
     * @param array
     */
    public static void selectSort(int[] array) {
        for (int i = 0; i < array.length-1; i++) {
            int minIndex = i;
            for (int j = i+1; j < array.length; j++) {
                if(array[j] < array[minIndex]) {
                    minIndex = j;
                }
            }
            swap(array,minIndex,i);
        }
    }
    public static void swap(int[] array,int i,int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

选择排序有 第二种写法,  反正都要遍历一遍数组, 不如把最大值和最小值都找出来, 最小的往最前放, 最大的往最后放. 

第二种写法步骤:

 1.  定义 left = 0, right = array.length-1, i = left + 1; minIndex = maxIndex = 0;

2.   while( left < right )循环,  i 在循环中, 遍历数组 找到最小元素下标 和 最大元素下标, 出循环 与 minIndex 和 maxIndex 交换.  

3. 出循环后 考虑一个特殊情况 , 当 left = 0 下标 对应的值就是最大值时, 第二步出循环后的交换操作 将最大值交换到minIndex下标处了. 需要 再将 maxIndex 标记到最大值.

第二种写法代码实现:

public static void swap(int[] array,int i,int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }
    //选择排序的第二种写法.
    public static void selectSort2(int[] array) {
        int left = 0;
        int right = array.length-1;
        while(left < right) {
            int minIndex = left;
            int maxIndex = left;
            for (int i = left; i <= right; i++) {
                if(array[i] < array[minIndex]) {
                    minIndex = i;
                }
                if(array[i] > array[maxIndex]) {
                    maxIndex = i;
                }
            }
            swap(array,left,minIndex);
            //有一种情况是maxIndex原本就在left位置上,上面的交换将最大值交换到minIndex下标处了.导致后续的排序不正确.
            if(maxIndex == left) {
                maxIndex = minIndex;
            }
            swap(array,right,maxIndex);
            left++;
            right--;
        }
    }

第四步 直接选择排序的特性总结:

1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:不稳定

2.2.3 堆排序

堆排序 (Heapsort) 是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。
第一步  具体步骤:
1. 排升序, 建大堆 , 向下调整算法 , parent 从 (array.length - 1)/2 开始 减到 1 
2. 将最后一个元素与首元素交换, 除末尾元素外, 其余元素调整成大根堆
3. 定义一个end = array.length - 1; while (end > 0) 循环中进行 2 步骤.
第二步  代码实现: 
 /**
     *堆排序
     *
     * 时间复杂度:O(N)+O(N*logN) 约等于 O(N*logN)
     *
     * 如果都以最坏的情况来看,堆排序是目前来说最快的,希尔排序其次.
     * 假设希尔排序为O(N^1.3) 当N越大时,logN趋于不变所以,堆排序O(N*logN)要快一些.
     *
     *  空间复杂度:O(1)
     *  稳定性:不稳定
     */
    public static void heapSort(int[] array) {
        //建大堆
        createBigHeap(array);//O(N)
        int end = array.length-1;
        //O(N*logN)
        while(end > 0) {
            swap(array,0,end);

            shiftDown(array,0,end);

            end--;
        }
    }
    private static void createBigHeap(int[] array) {
        for (int parent = (array.length-1-1)/2; parent >= 0; parent--) {
            shiftDown(array,parent,array.length);
        }
    }
    private static void shiftDown(int[] array,int parent,int end) {
        int child = (parent*2)+1;
        while(child < end) {
            //保证右子树存在并且当右子树大的时候,child++来到右子树的下标.
            if(child+1 < end && array[child+1] > array[child]) {
                child++;
            }
            if(array[child] > array[parent]) {
                swap(array,child,parent);
                parent = child;
                child = parent*2+1;
            }else {
                //本身就是大根堆
                break;
            }
        }
    }

第三步   堆排序的特性总结:

1. 堆排序使用堆来选数,效率就高了很多。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(1)
4. 稳定性:不稳定

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

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

相关文章

HARCT 2025 新增分论坛7:机器人和自动化的新趋势

会议名称&#xff1a;机电液一体化与先进机器人控制技术国际会议 会议简称&#xff1a;HARCT 2025 大会时间&#xff1a;2025年1月3日-6日 大会地点&#xff1a;中国桂林 主办单位&#xff1a;桂林航天工业学院、广西大学、桂林电子科技大学、桂林理工大学 协办单位&#…

海洋通信船舶组网工业4G路由器应用

船舶是浩瀚海洋中探索与贸易的载体&#xff0c;更是船员们生活与工作的家园。为了在广阔的水域中搭建起稳定、高效的网络桥梁&#xff0c;工业4G路由器以卓越的通信组网能力&#xff0c;为船舶组网提供网络支持。 工业4G路由器以其强大的信号发射能力&#xff0c;确保船舶内部…

《Vue零基础入门教程》第三课:起步案例

往期内容 《Vue零基础入门教程》第一课&#xff1a;Vue简介 《Vue零基础入门教程》第二课&#xff1a;搭建开发环境 做为第一个案例, 主要给大家介绍vue的最基本使用. vue使用的3步曲(重点) 引入vue.js编写页面(视图)创建App实例并挂载 1) 引入vue.js 在html的头部, 通过…

【unity小技巧】一些unity3D灯光的使用与渲染及性能优化方案

文章目录 天空盒反射配置太阳耀斑眩光烘培光照烘培光照时弹出错误&#xff0c;记得勾选模型下面的选择阴影项目配置光源模型模型shader的问题 全局光照混合光照模式混合照明模式减性照明模式Shadowmask照明模式间接烘焙照明模式 环境光遮罩灯光探针反射探针技术关闭反射探针可以…

【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录

背景 Jetbrain IDE 支持生成 Test 类&#xff0c;其中选择JUnit5 和 JUnit&#xff0c;但是感觉这不是标准的单元测试&#xff0c;因为接口命名吧。 差异对比 两者生成的单测API名称同原API&#xff0c;没加test前缀的。使用差异主要表现在&#xff1a; setUp &#xff06; …

Nuxt3:拉取项目模板失败问题解决方法

问题描述 使用官网提供的命令npx nuxilatest init <project-name> 创建Nuxt3项目时&#xff0c;遇到了拉取项目模板失败的问题&#xff0c;报错信息如下 先分析一下这行命令在做的事情&#xff0c;结合之前的经验来看&#xff0c;似乎是在尝试通过该网址返回的元数据信息…

在 Sui 区块链上创建、部署与测试自定义 move _coin合约的完整教程

系列文章目录&#x1f60a; Task1&#xff1a;hello_move&#x1f340; Task2&#xff1a;move_coin&#x1f340; Task3&#xff1a;move_nft&#x1f340; 目录 系列文章目录&#x1f60a;引言一、更新本地代码1、查看当前项目的远程仓库信息。2、将远程仓库的最新代码同步到…

三层交换机静态路由实验

1、前置知识 2、实验目的 3、实验器材&#xff1a; 3560-23PS交换机2台、主机4台、交叉线1根和直通网线4根。 4、实验规划及拓扑 实验要求&#xff1a; &#xff08;1&#xff09;在交换机A和交换机B上分别划分基于端口的VLAN&#xff1a; 交换机 VLAN 端口成员 交换机…

iOS构建版本以及Hbuilder打iOS的ipa包全流程

目录 Hbuilder打ipa包 打包之前进行应用配置 应用版本号设置 使用广告标识设置 iOS-云打包 下载并转移安装包 使用Transporter提交版本 应用简介 下载应用 账号登录 提交安装包到apple store connect 在apple开发者平台上确认 总结 本篇文章详细的介绍了使用Hbuil…

java学习-集合

为什么有集合&#xff1f; 自动扩容 数组&#xff1a;长度固定&#xff0c;可以存基本数据类型和引用数据类型 集合&#xff1a;长度可变&#xff0c;可以存引用数据类型&#xff0c;基本数据类型的话需要包装类 ArrayList public class studentTest {public static void m…

返回流类型接口的错误信息处理

返回流类型接口的错误信息处理 前言axios拦截器src/utils/request.ts对应接口 前言 返回流类型接口需要在响应成功回调里拦截&#xff0c;且该接口的status始终是200&#xff0c;尽管后端返回的code可能是非2xx&#xff0c;因此返回流类型的接口&#xff0c;其错误信息需要单独…

用宏实现简单的计算器

大家好&#xff0c;那么经过我们前面几期的学习&#xff0c;我们对宏有了一定的了解&#xff0c;那么我们今天就来试试实现一个简单的加减乘除运算。 我们的思路是使用三目操作符来分别进行加减和乘除的运算&#xff0c;然后用if判断来”进入相关的判断体进而来进行计算。当然…

WEB攻防-通用漏洞文件上传js验证mimeuser.ini语言特性

知识点&#xff1a; 1、文件上传-前端验证 2、文件上传-黑白名单 3、文件上传-user.ini妙用 4、文件上传-php语言特性 详细点&#xff1a; 1、检测层面&#xff1a;前端&#xff0c;后端等 2、检测内容&#xff1a;文件头&#xff0c;完整型&#xff0c;二次渲染等 3、检…

《Vue零基础教程》(3)创建第一个应用案例

1 应用实例 参考官方文档 https://cn.vuejs.org/api/application.html#create-app 示例 const {createApp} Vue// 通过createApp创建一个应用实例 const app createApp({/* 选项 */ }) console.log(app) 分析打印结果, 可知 应用实例是一个对象没有_开头的是公开属性/方…

复合瓦片切片集集合数量与性能关系验证

作者&#xff1a;lzzzz Sci瓦片聚合性能分析 需要聚合的图层越多&#xff0c;性能越低&#xff0c;目前测试以每个瓦片仅包含一个矢量面数据为例&#xff1a; sci数量 服务拉起耗时 前端加载&#xff08;单瓦片&#xff09; 100 10s 500ms 1000 5min 3s 10000 1hour …

大数据-231 离线数仓 - DWS 层、ADS 层的创建 Hive 执行脚本

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; Java篇开始了&#xff01; 目前开始更新 MyBatis&#xff0c;一起深入浅出&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff0…

摄影:相机控色

摄影&#xff1a;相机控色 白平衡&#xff08;White Balance&#xff09;白平衡的作用&#xff1a; 白平衡的使用环境色温下相机色温下总结 白平衡偏移与包围白平衡包围 影调 白平衡&#xff08;White Balance&#xff09; 人眼看到的白色&#xff1a;会自动适应环境光线。 相…

性能监控利器:Ubuntu 22.04 上的 Zabbix 安装与配置指南

简介 今天我们来聊聊如何在 Ubuntu 22.04 上安装和配置 Zabbix。我们会用到 PostgreSQL 作为数据库后端&#xff0c;Nginx 作为 Web 服务器&#xff0c;并用 Let’s Encrypt SSL 证书来保驾护航。 什么是 Zabbix&#xff1f; Zabbix 是一个开源的网络监控和管理解决方案&…

队列基本实现

模板 int queue[10010]; int hh1,tt0; void push1(int x) {queue[tt]x; } void pop1() {if(hh>tt){cout<<"ERR_CANNOT_POP"<<endl;}else{hh;} } int query1() {if(hh>tt){cout<<"ERR_CANNOT_QUERY"<<endl;}return queue[hh…

【ArcGISPro】使用AI模型提取要素-提取车辆(目标识别)

示例数据下载 栅格数据从网上随便找一个带有车辆的栅格数据 f094a6b1e205cd4d30a2e0f816f0c6af.jpg (1200799) (588ku.com) 添加数据