七大排序算法(Java实现)——冒泡、快排、插入、希尔、选择、堆排、归并

升序排序为例

交换元素的通用代码:

/**
     * 交换元素
     * @param arr
     * @param idx1
     * @param idx2
     */
    private void swap(int[] arr, int idx1, int idx2) {
        int tmp = arr[idx1];
        arr[idx1] = arr[idx2];
        arr[idx2] = tmp;
    }

一、交换排序——冒泡排序

  • 冒泡排序:
  • 相邻两个元素两两比较,若逆序则交换
  • 每一轮会确定一个最大值到它最后的位置上,只需n-1轮排序
  • 优化:如果某一轮排序从头至尾没有发生交换,说明已经有序,结束
/**
     * 交换排序:稳定,O(n^2)
     * 1. 冒泡排序:
     * 相邻两个元素两两比较,若逆序则交换
     * 每一轮会确定一个最大值到它最后的位置上,只需n-1轮排序
     * 优化:如果某一轮排序从头至尾没有发生交换,说明已经有序,结束
     * @param array 待排序列
     */
    public void bubble_Sort(int[] array) {
        int n = array.length;
        for(int i = 0; i < n - 1; i++) { // n-1轮排序
            boolean flag = true;
            for (int j = 0; j < n - 1 -i; j++) { // 每轮会确定一个最值
                if(array[j] > array[j + 1]) {  // 逆序则交换
                    swap(array, j, j + 1);
                    flag = false;  // 本轮发生了交换
                }
            }
            if (flag) { // 提前结束
                break;
            }
        }
    }

二、交换排序——快速排序

  • 快速排序:不稳定,O(nlogn)
  • 随机选出一个元素作为基准pivot,此处选第一个元素array[0]
  • 从序列的两端开始探测,哨兵 i 从前向后找比pivot大的,哨兵 j 从后向前找比pivot小的,然后交换i和j指向的元素;每次都让哨兵j先动
  • 直到哨兵相遇,交换相遇点和pivot
  • 以pivot为基准前后分区,递归左右两个子序列

为什么每次要让哨兵j先动?因为pivot取在左侧

        左侧哨兵i先动,i会找到第一个大于pivot的位置,但哨兵j可能会因为 i小于j 的限制而没有落在小于pivot的位置上,提前结束探测而与i相遇,此时交换,结果是错误的
/**
     * 交换排序:
     * 2. 快速排序:不稳定,O(nlogn)
     * 随机选出一个元素作为基准pivot,此处选第一个元素array[0]
     * 从序列的两端开始探测,哨兵 i 从前向后找比pivot大的,哨兵 j 从后向前找比pivot小的,然后交换i和j指向的元素;每次都让哨兵j先动
     * 直到哨兵相遇,交换相遇点和pivot
     * 以pivot为基准前后分区,递归左右两个子序列
     *
     * 为什么每次要让哨兵j先动?因为pivot取在左侧
     * 左侧哨兵i先动,i会找到第一个大于pivot的位置,但哨兵j可能会因为 i小于j 的限制而没有落在小于pivot的位置上,提前结束探测而与i相遇,此时交换,结果是错误的
     * @param array
     */
    public void quick_Sort(int[] array, int low, int high) {
        if (low >= high) {
            return;
        }
        int pivot = array[low]; // 基准
        int i = low; // 哨兵
        int j = high;

        while(i < j) {
            // i < j 确保哨兵在相同处停下(i == j),防止越界
            while (array[j] >= pivot && i < j) {  //从右往左找比基准值小的数
                j--;
            }
            while (array[i] <= pivot && i < j) {  //从左往右找比基准值大的数
                i++;
            }
            if (i < j) {
                swap(array, i, j);
            }
        }
        array[low] = array[j]; // 交换基准与哨兵相遇处的元素
        array[j] = pivot;
        quick_Sort(array, low, j - 1);
        quick_Sort(array, j + 1, high);
    }

三、插入排序——直接插入

  • 直接插入:稳定,O(n^2)
  • 将array[0]视为初始的有序表,从下一个元素开始,逐个插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表
  • 双层循环:外层循环遍历除array[0]外的所有元素,内层循环对当前元有序表进行待插入位置查找(从后向前),并进行移动
/**
     * 插入排序
     * 1. 直接插入:稳定,O(n^2)
     * 将array[0]视为初始的有序表,从下一个元素开始,逐个插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表
     * 双层循环:外层循环遍历除array[0]外的所有元素,内层循环对当前元有序表进行待插入位置查找(从后向前),并进行移动
     * @param array
     */
    public void insert_Sort(int[] array) {
        int n = array.length;
        for(int i = 1; i < n; i++) {
            for(int j = i; j >= 1; j--) {
                if(array[j] < array[j - 1]) { // 从后向前比较,找到插入的位置
                    swap(array, j, j - 1);
                }else {                      // 比有序表的最后一个元素都大,直接插在末尾
                    break;
                }
            }
        }
    }

四、插入排序——希尔排序

  • 希尔排序:不稳定,O(n^1.3)
  • 直接插入的改进:分组,组内插入排序
  • 根据增量对序列进行分组,在组内通过直接插入排序,每个组都排好序后,增量减半,再次分组排序,直到增量为1
/**
     * 插入排序:
     * 2. 希尔排序:不稳定,O(n^1.3)
     * 直接插入的改进:分组,组内插入排序
     * 根据增量对序列进行分组,在组内通过直接插入排序,每个组都排好序后,增量减半,再次分组排序,直到增量为1
     * @param array
     */
    public void shell_Sort(int[] array) {
        for(int gap = array.length / 2; gap >=1; gap = gap / 2) {  // gap初始为长度/2, 后逐步/2
            for (int i = gap; i < array.length; i++) {  // 从每个分组中的第二个元素开始,进行直接插入排序
                int tmp = array[i];            // 当前待插入的元素
                int j = i - gap;              // 从后向前比较有序表,j为当前比较到哪个下标了

                while(j >= 0 && array[j] > tmp) {
                    array[j + gap] = array[j];  // 逆序,a[j] 后移
                    j -= gap;
                }
                array[j + gap] = tmp; // while结束,j是不满足的下标,需+gap,将tmp插在此处
            }
        }
    }

五、选择排序——简单选择

  • 简单选择排序:不稳定, O(n^2)
  • 每轮在待排序列中找min,将其添加至有序表的末尾
/**
     * 选择排序:
     * 1. 简单选择排序:不稳定, O(n^2)
     * 每轮在待排序列中找min,将其添加至有序表的末尾
     * @param array
     */
    public void select_Sort(int[] array) {
        int n = array.length;
        for(int i = 0; i < n - 1; i++) { // 当前待插入位置,有序表的最末max
            int min_index = i;
            for(int j = i + 1; j < n; j++) {  // 找min的下标
                if (array[min_index] > array[j]) {
                    min_index = j;
                }
            }
            if (min_index != i) {
                swap(array, i, min_index);
            }
        }
    }

六、选择排序——堆排序

  • 堆排序:升序使用大根堆(父节点大于左右孩子的完全二叉树),不稳定,O(nlogn)
  • 将待排序列构造小根堆:建堆O(n),然后调整成一个小根堆
  • 将堆顶与最后一个元素交换,再从堆顶开始调整,将原堆顶脱离大根堆,即序列的max,逐步得到当前堆的堆顶max
/**
     * 选择排序:
     * 2. 堆排序:升序使用大根堆(父节点大于左右孩子的完全二叉树),不稳定,O(nlogn)
     * 将待排序列构造小根堆:建堆O(n),然后调整成一个小根堆
     * 将堆顶与最后一个元素交换,再从堆顶开始调整,将原堆顶脱离大根堆,即序列的max,逐步得到当前堆的堆顶max
     *
     * @param array
     */
    public void heap_Sort(int[] array) {
        int n = array.length;
        // 建堆:向下调整建堆O(n)和向上调整建堆O(nlogn)
        // 子节点i的父节点下标:(i- 1)/2,从最后一个节点父节点开始
        for (int i = (n - 1 - 1) / 2; i >= 0; i--) { // 从后向前,可以保证子树已经是大根堆,这样才能使用向下调整
            heapify(array, n, i);
        }

        // 堆顶与最末交换,调整
        for (int i = n - 1; i >= 0; i--) {
            swap(array, n - 1, 0);  // 交换
            n--;                     // 使交换至末尾的max脱离堆
            heapify(array, n, 0); // 调整堆
        }
    }

    /**
     * 调整、维护堆(大根堆)
     * @param array
     * @param n  数组长度
     * @param i 待维护节点下标
     */
    public void heapify(int[] array, int n ,int i) {
        int lson = 2 * i + 1;
        int rson = 2 * i + 2;
        int max_index = i; // 假设当前节点是最小值

        // 找到最小节点的下标
        if(lson < n && array[max_index] < array[lson]) {  // min_index > 左,调整
            max_index = lson;
        }
        if (rson < n && array[max_index] < array[rson]) {  // min_index > 右,调整
            max_index = rson;
        }
        if (max_index != i) {  // 父节点不是min,与min交换调整
            swap(array, max_index, i);
            heapify(array, n, max_index);  // 调整后可能导致子树不符合规定,递归调整
        }
    }

七、归并排序

  • 归并排序:稳定,O(nlogn)
  • 分治思想
  • 将序列从中间一分为二,递归拆分,直到包含单一元素,将其视为有序表
  • 从左右数组中选择小的元素放入到临时空间,后移下标,直到某一数组的下标达到尾部,将另一序列剩下的所有元素依次放入临时空间,将临时空间的数据依次放入原数据数组
/**
     * 归并排序:稳定,O{nlogn)
     * 分治思想:将序列从中间一分为二,递归拆分,直到包含单一元素,将其视为有序表
     * 从左右数组中选择小的元素放入到临时空间,后移下标,直到某一数组的下标达到尾部,将另一序列剩下的所有元素依次放入临时空间,将临时空间的数据依次放入原数据数组
     * @param array
     */
    public void merge_Sort(int[] array, int left, int right, int[] tmp_array) {
        if (left >= right) {  // 递归出口:每组只有一个元素left == right
            return;
        }
        int mid = (right + left) / 2;
        merge_Sort(array, left, mid, tmp_array);
        merge_Sort(array, mid+1, right, tmp_array);
        merge(array, left, mid, right, tmp_array);  // 合并

    }

    public void merge(int[] array, int left, int mid, int right, int[] tmp_array) {
        int i = left;  // i j 分别指向两个数组的第一个元素
        int j = mid + 1;
//        int[] tmp_array = array.clone();  //克隆,遍历tmp_array,改变array——>占用空间,传入一个克隆,但注意每次合并完后更新tmp_array与array保持一致
        int tmp_index = left;

        while(i <= mid && j <= right) {
            if(tmp_array[i] < tmp_array[j]) {
                array[tmp_index] = tmp_array[i];
                i++;
                tmp_index++;
            }else {
                array[tmp_index] = tmp_array[j];
                j++;
                tmp_index++;
            }
        }
        // 其中一个序列结束了,但另一个还有元素,直接加入(肯定是有序的)
        while (i <= mid) {
            array[tmp_index] = tmp_array[i];
            i++;
            tmp_index++;
        }
        while (j <= right) {
            array[tmp_index] = tmp_array[j];
            j++;
            tmp_index++;
        }
        // 一次合并后临时数组要和nums同步
        for(i = left; i <= right; i++)
            tmp_array[i] = array[i];
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【STM32+HAL】读取电池电量

一、准备工作 有关CUBEMX的初始化配置&#xff0c;参见我的另一篇blog&#xff1a;【STM32HAL】CUBEMX初始化配置 有关定时器触发ADC模式配置&#xff0c;详见【STM32HAL】ADC采集波形实现 有关软件触发ADC模式配置&#xff0c;详见【STM32HAL】三轴按键PS2摇杆 二、所用工具…

Python赋值运算符

目录 赋值运算符 将值赋给变量&#xff1a; 做加法运算之后完成赋值&#xff1a; 做减法运算之后完成赋值&#xff1a;- 做乘法运算之后完成赋值&#xff1a;* 做除法运算之后完成赋值&#xff1a;/ 做整除运算之后完成赋值&#xff1a;// 做幂次运算之后完成赋值&#xff1a;*…

自动驾驶框架 UniAD环境部署

感谢大佬们的开源工作 UniAD-github地址-YYDS更多bev算法部署参考如果您觉得本帖对您有帮助&#xff0c;感谢您一键三连支持一波^_^ 统一自动驾驶框架 (UniAD) &#xff0c;第一个将全栈驾驶任务整合到一个深度神经网络中的框架&#xff0c;并可以发挥每个子任务以及各个模块的…

【MySQL 数据宝典】【索引原理】- 004 优化示例-join in exist

一、join 优化原理 1.1 基本连接方式介绍 JOIN 是 MySQL 用来进行联表操作的&#xff0c;用来匹配两个表的数据&#xff0c;筛选并合并出符合我们要求的结果集。 1.2 驱动表的定义 1.2.1 什么是驱动表 多表关联查询时,第一个被处理的表就是驱动表,使用驱动表去关联其他表.驱…

基于springboot的考勤管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

Zynq 7000 系列中成功执行BootROM的条件

Zynq 7000设备的启动需要正确的电压序列和I/O引脚控制。BootROM的流程由复位类型、启动模式引脚设置以及启动映像来控制。BootROM对所选启动设备的引脚连接有特定的要求。 Zynq 7000 SoC设备具有电源、时钟和复位要求&#xff0c;这些要求必须得到满足&#xff0c;才能成功执行…

java:SpringBootWeb请求响应

Servlet 用java编写的服务器端程序 客户端发送请求至服务器 服务器启动并调用Servlet,Servlet根据客户端请求生成响应内容并将其传给服务器 服务器将响应返回给客户端 javaweb的工作原理 在SpringBoot进行web程序开发时,内置了一个核心的Servlet程序DispatcherServlet,称之…

RocketMQ快速入门:namesrv、broker、dashboard的作用及消息发送、消费流程(三)

0. 引言 接触rocketmq之后&#xff0c;大家首当其冲的就会发现需要安装3个组件&#xff1a;namesrv, broker, dashboard&#xff0c;其中dashboard也叫console&#xff0c;为选装。而这几个组件之前的关系是什么呢&#xff0c;消息发送和接收的过程是如何传递的呢&#xff0c;…

应用实战 | 别踩白块小游戏,邀请大家来PK挑战~

“踩白块会输”是一个简单的微信小程序游戏&#xff0c;灵感来自当年火热的别踩白块游戏&#xff0c;程序内分成三个模块&#xff1a;手残模式、经典模式和极速模式&#xff0c;分别对应由易到难的三种玩法&#xff0c;可以查看游戏排名。动画效果采用JS实现&#xff0c;小程序…

Spark-机器学习(6)分类学习之支持向量机

在之前的文章中&#xff0c;我们学习了分类学习之朴素贝叶斯算法&#xff0c;并带来简单案例&#xff0c;学习用法。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢…

基于YOLOV8+Pyqt5无人机航拍太阳能电池板检测系统

1.YOLOv8的基本原理 YOLOv8是一种前沿的目标检测技术&#xff0c;它基于先前YOLO版本在目标检测任务上的成功&#xff0c;进一步提升了性能和灵活性&#xff0c;在精度和速度方面都具有尖端性能。在之前YOLO 版本的基础上&#xff0c;YOLOv8 引入了新的功能和优化&#xff0c;…

SpringBoot 常用注解总结超详细(面试)

目录 一、组件相关&#x1f381; Controller Service Repository Component 二、依赖注入相关&#x1f349; Autowired Resource 根据类型注入&#xff08;By Type&#xff09; 根据名称注入&#xff08;By Name&#xff09; 区别 Qualifier Resource 和 Qualifie…

C语言浮点型数据在内存中的存储及取出等的介绍

文章目录 前言一、浮点型在内存中的存储二、浮点数存储规则三、浮点数在内存中的存储&#xff08;32位&#xff09;float类型四、浮点数在内存中的存储&#xff08;64位&#xff09;double类型五、指数E从内存中取出分成三种情况1. E不全为0或不全为12. E全为03. E全为1 六、有…

设计模式之工厂模式FactoryPattern(二)

一、简单工厂 package com.xu.demo.factoryPattern;/*** 简单工厂模式类*/ public class SimpleFactoryPattern {public static Phone create(String name) {//根据输入对象名称判断返回相匹配的对象if("IPhone".equals(name)) {//返回对象return new IPhone();}else…

Java算法--队列

队列 队列介绍 队列是一个有序列表&#xff0c;可以用数组或是链表来实现。遵循先入先出的原则。即&#xff1a;先存入队列的数据&#xff0c;要先取出。后存入的要后取出 数组模拟队列思路 队列本身是有序列表&#xff0c;若使用数组的结构来存储队列的数据&#xff0c;则…

自动驾驶新书“五一”节马上上市了

我和杨子江教授合写的《自动驾驶系统开发》终于在清华大学出版社三校稿之后即将在五一节后出版。 清华大学汽车学院的李克强教授和工程院院士撰写了序言。 该书得到了唯一华人图灵奖获得者姚期智院士、西安交大管晓宏教授和科学院院士以及杨强教授和院士等的推荐&#xff0c;…

git变更远端仓库名之后如何修改本地仓库配置的另一种方法?(删remote指针、添加、绑定master)

背景 如果某个远端的仓库地址变化后&#xff0c;本地仓库可以修改对应的remote。 之前谈过几种方法&#xff0c;比如重新设置一个新的remote的指针&#xff0c;绑定到新地址。然后删除origin&#xff0c;然后把新指针mv到origin。比如直接seturl修改&#xff08;git remote se…

基于HTML+CSS+JavaScript的表白网页

基于HTMLCSSJavaScript的表白网页 前言效果截图&#xff08;为GIF格式&#xff09;部分代码领取源码下期更新预报 前言 大部分人都有喜欢的人&#xff0c;学会这个表白代码&#xff0c;下次表白你肯定会成功。 效果截图&#xff08;为GIF格式&#xff09; 部分代码 index.htm…

使用 Python 和 DirectShow 从相机捕获图像

在 Python 中使用 OpenCV 是视觉应用程序原型的一个非常好的解决方案,它允许您快速起草和测试算法。处理从文件中读取的图像非常容易,如果要处理从相机捕获的图像,则不那么容易。OpenCV 提供了一些基本方法来访问链接到 PC 的相机(通过对象),但大多数时候,即使对于简单的…

在no branch上commit后,再切换到其他分支,找不到no branch分支的修改怎么办?

解决办法 通过git reflog我们可以查看历史提交记录&#xff0c;这里的第二条提交&#xff08;fbd3ea8&#xff09;就是我在no branch上的提交。 再通过git checkout -b backup fbd3ea8&#xff0c;恢复到上次提交的状态&#xff0c;并且为其创建个分支backup&#xff0c;此时…
最新文章