七大排序算法详解

1.概念

1.排序的稳定性

常见的稳定的排序有三种:直接插入排序,冒泡排序,归并排序

对于一组数据元素排列,使用某种排序算法对它进行排序,若相同数据之间的前后位置排序后和未排序之前是相同的,我们就成这种排序算法具有稳定性

单看单个属性的稳定性并无意义,稳定性主要体现在对具有多个属性的对象进行排序时才有意义
假设一个订单对象有两个属性,分别是下单时间与下单金额:
此时我们有一个需求,就是按照订单金额从高到低排序,若金额相同,则按照下单的先后时间排序

  • 方法一:就是先按照订单金额大小排序,然后把金额相同的订单再次按照时间排序,但是这样就需要进行多次排序
  • 方法二:如果我们先把订单按照下单时间的先后排好序,然后再按照订单金额排序,此时如果我们使用的排序是稳定性的,那么它排序后同样是按照下单顺序先后排列的,此时我们就只需要两次排序

所以排序的稳定性也是非常重要

2.排序分类

1.外部排序(不牵扯元素大小比较)

外部排序主要有,桶排序,计数排序,基数排序,时间复杂度近乎O(n)
这三种排序的集合元素非常大,内存放不下,需要使用外部存储器来存储排序,并且对数据的要求非常严格

2.内部排序(基于比较的方式)

在这里插入图片描述

1.插入排序

⭐直接插入排序(稳定)

1.核心思路

每次从无序区间中选择第一个元素,插入到有序区间的合适位置(相当于打扑克牌码牌的过程),有序区间+1,无序区间-1,直至整个数组有序
在这里插入图片描述
⭐⭐⭐直接插入排序在近乎有序的集合性能上非常好(是因为近乎有序的集合,不需要频繁的去交换),常常作为其他高阶排序的优化手段

2.详细代码
    /**
     * 直接插入排序
     * @param nums
     */
    public static void insertSort(int[] nums) {
        // 有序区间[0,i)
        // 无序区间[i,n)
        for (int i = 1; i < nums.length; i++) {
            for (int j = i; j-1 >= 0; j--) {
                // 若当前值比前一个位置值还小,交换位置
                if (nums[j] < nums[j-1]) {
                    swap(nums, j , j-1);
                }
            }
        }
    }

⭐希尔排序

希尔排序是对插入排序的优化,因为插入排序在近乎有序的数组上性能很好,希尔排序正是利用了这一点

1.核心思路

希尔排序不断地将原数组分为若干个子数组,先将子数组内部调整为有序,不断变大分组的长度,当分组长度为1时(一个元素天然有序),此时进行一次插入排序即可

在这里插入图片描述

2.详细代码
   // 希尔排序
    public static void shellSort(int[] arr) {
        int gap = arr.length >> 1;
        while (gap > 1) {
            // 先按照gap分组,组内使用插入排序
            insertionSortByGap(arr,gap);
            gap = gap >> 1;
        }
        // 当gap == 1时,整个数组接近于有序,此时来一个插入排序
        insertionSortByGap(arr,1);
    }

    // 按照gap分组,组内的插入排序
    private static void insertionSortByGap(int[] arr, int gap) {
    	// i初始化为0也可以,更容易理解
        for (int i = gap; i < arr.length; i++) {
            for (int j = i; j - gap >= 0 && arr[j] < arr[j - gap] ; j -= gap) {
                swap(arr,j,j - gap);
            }
        }
    }

2.选择排序

⭐直接选择排序(不稳定)

1.核心思路

每次在无序区间选择最小值与无序区间第一个位置元素交换,有序区间+1,无序区间-1,直至整个数组有序
在这里插入图片描述

2.详细代码
    /**
     * 直接选择排序
     * @param nums
     */
    public static void selectionSort(int[] nums) {
        // 起始状态:有序区间[0,i)
        // 无需区间[i,n)
        for (int i = 0; i < nums.length - 1; i++) {
            // 指向当前无需区间最小值的下标
            int minIndex = i;
            for (int j = i+1; j < nums.length; j++) {
                // 若当前值比最小值还小
                if (nums[j] < nums[minIndex]) {
                    // 更新最小值下标
                    minIndex = j;
                }
            }
            // 此时minIndex指向无需区间的最小值,将其加载有序区间的后面,有序区间+1,无序区间-1
            swap(nums, i, minIndex);
        }
    }

⭐堆排序

堆排详情博客:
原地堆排序

3.交换排序

⭐冒泡排序(稳定)

1.核心思路

每次遍历将无序数组的最大元素交换至末尾,无序数组-1,有序数组+1,直至整个数组有序

2.详细代码
    // 无序区间[0...i]
    // 有序区间[]
    public static void bubbleSort(int[] arr) {
        // 外层循环表示要进行元素操作的趟数
        for (int i = 0; i < arr.length - 1; i++) {
            boolean isSwaped = false;
            for (int j = 0; j < arr.length - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    isSwaped = true;
                    swap(arr,j,j + 1);
                }
            }
            if (!isSwaped) {
                break;
            }
        }
    }

⭐快速排序(不稳定)

1.核心思路

每次从无序数组中选取一个元素作为分区点(pivot),将原集合中所有<pivot的元素都放在分区点的左侧,将所有>pivot的元素都放在分区点的右侧,这样分区点最终位置就确定了,继续在左半区间和右半区间重复此过程即可
在这里插入图片描述

快速排序和直接选择排序不同的是在快排选择的过程中在不断地调整元素的顺序,在这个过程中原数组已经不断有序了

2.详细代码
    /**
     * 快速排序(挖坑法)
     */
    public static void quitSort(int[] nums) {
        quitSortInternal(nums, 0, nums.length-1);
    }

    private static void quitSortInternal(int[] nums, int l, int r) {
        if (l >= r) {
            return;
        }
        // 分区之后,返回已经放好位置的元素下标
        int p = quitSortByHole(nums, l, r);
        // 将放好位置元素的左侧丢进去排序
        quitSortInternal(nums, l, p - 1);
        // 在将放好位置元素的右侧丢进去排序
        quitSortInternal(nums, p + 1, r);
        // 此时数组就整体有序了
    }
        /**
     * 使用非递归的方式
     * @param nums
     * @param l
     * @param r
     */
    private static void quitSortInternal1(int[] nums, int l, int r) {
        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(r);
        stack.push(l);
        while (!stack.isEmpty()) {
            // 获取左右区间
            int i = stack.pop();
            int j = stack.pop();
            if (i >= j) {
                continue;
            }
            // 调用获得一个位置的结果
            int p = quitSortByHole(nums, i, j);
            // 将左边压入栈中
            stack.push(p-1);
            stack.push(i);
            // 右边压入栈中
            stack.push(j);
            stack.push(p+1);
        }

    }

    private static int quitSortByHole(int[] nums, int l, int r) {
        // 分区数字
        int pivot = nums[l];
        int i = l;
        int j = r;
        while (i < j) {
            while (i < j && nums[j] >= pivot) {
                j--;
            }
            // 走到这说明nums[j] < pivot
            nums[i] = nums[j];
            while (i < j && nums[i] <= pivot) {
                i++;
            }
            // 走到这说明nums[i] > pivot
            nums[j] = nums[i];
        }
        // 出循环说明i==j
        // 将pivot写回i
        nums[i] = pivot;
        // 返回分区点最终位置
        return i;
    }

4.归并排序

⭐归并排序(稳定,时间复杂度O(nlogn),空间复杂度O(n))

对原始数据不敏感

1.核心思路

1. 先不断地将原数组一分为二,直至子数组长度为1
2. 不断地将两个相邻的有序子数组合并成一个更大的有序子数组,直至合并后的数组与原数组长度相同,此时排序完成
在这里插入图片描述
核心操作:合并两个子数组merge(nums, l, mid, r)
int i=l代表左边数组走到的下标,
int j=mid+1代表右边数组走到的下标,
k代表当前原数组合并到哪个位置

2.归并排序应用

在这里插入图片描述

3.详细代码
    /**
     * 归并排序
     */
    public static void mergeSort(int[] nums) {
        mergeSortInternal(nums, 0, nums.length-1);
    }

    /**
     * 在nums的l-r内进行归并排序
     * @param nums
     * @param l
     * @param r
     */
    private static void mergeSortInternal(int[] nums, int l, int r) {
        if (l >= r) {
            return;
        }
        int mid = l + ((r - l) >> 1);;
        // 将左边和右边丢到merge
        mergeSortInternal(nums, l, mid);
        mergeSortInternal(nums, mid+1, r);
        // 此时说明左右两个数组都已经排好序
        merge(nums, l, mid, r);
    }

    private static void merge(int[] nums, int l, int mid, int r) {
        // 创建一个大小与r-l+1一样大的数组
        int[] aux = new int[r - l + 1];
        System.arraycopy(nums, l, aux, 0, r - l + 1);
        int i = l;
        int j = mid+1;
        // i代表左边数组走到的下标,j代表右边数组走到的下标,k代表当前原数组合并到哪个位置
        for (int k = l; k <= r; k++) {
            if (i > mid) {
                // 说明左边的数组已经走完,把右边的全部写回原数组即可
                nums[k] = aux[j-l];
                j++;
            } else if (j > r) {
                // 说明右边的数组已经走完,把左边的全部写回原数组即可
                nums[k] = aux[i-l];
                i++;
            } else if (aux[i-l] <= aux[j-l]) {
                // 说明左边的数字小,把左边数组内的数字写回原数组
                nums[k] = aux[i-l];
                i++;
            } else {
                // 说明右边的数字小,把右边数组内的数字写回原数组
                nums[k] = aux[j-l];
                j++;
            }
        }
    }

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

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

相关文章

压力检测器的基本信息是什么

压力检测器利用了传感器技术、电路处理技术、无线传输技术&#xff0c;能够精准测量气体或者液体等介质的压力&#xff0c;并将测得的数据上传至监控平台。 压力检测器能够适用于供水厂、污水处理厂、消防水系统、输油管道、输气管道等相关场景&#xff0c;拥有自动补偿功能、…

Log4Qt日志框架(1)- 引入到QT中

Log4Qt日志框架&#xff08;1&#xff09;- 引入到QT中 1 下载源码2 简介3 加入到自己的项目中3.1 使用库文件3.2 引入源文件 4 说明 1 下载源码 github&#xff1a;https://github.com/MEONMedical/Log4Qt 官方(版本较老)&#xff1a;https://sourceforge.net/projects/log4q…

暄桐展览| 我们桐学有自己的习作展(1)

林曦老师《从书法之美到生活之美》的第五阶课程《静定的滋养2021》已告一段落。570天的用功&#xff0c;桐学们的技艺都有了水涨船高的进益。      无论书法课&#xff08;全阶和五阶&#xff09;还是国画课&#xff0c;暄桐都有一套完整系统的教学体系&#xff0c;也会在桐…

Linux下的Shell基础——正则表达式入门(四)

前言&#xff1a; 正则表达式使用单个字符串来描述、匹配一系列符合某个语法规则的字符串。在很多文本编辑器里&#xff0c;正则表达式通常被用来检索、替换那些符合某个模式的文本。 在Linux 中&#xff0c;grep&#xff0c;sed&#xff0c;awk 等文本处理工具都支持…

Java可视化物联网智慧工地SaaS平台源码:人脸识别考勤

基于微服务JavaSpring Cloud Vue UniApp MySql实现的智慧工地云平台源码 智慧工地是指利用云计算、大数据、物联网、移动互联网、人工智能等技术手段&#xff0c;为建筑施工现场提供智能硬件及物联网平台的解决方案&#xff0c;以实现建筑工地的实时化、可视化、多元化、智慧化…

用手势操控现实:OpenCV 音量控制与 AI 换脸技术解析

基于opencv的手势控制音量和ai换脸 HandTrackingModule.py import cv2 import mediapipe as mp import timeclass handDetector():def __init__(self, mode False, maxHands 2, model_complexity 1, detectionCon 0.5, trackCon 0.5):self.mode modeself.maxHands max…

FFmpeg<第一篇>:环境配置

1、官网地址 http://ffmpeg.org/download.html2、linux下载ffmpeg 下载&#xff1a; wget https://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2解压&#xff1a; tar xvf ffmpeg-snapshot.tar.bz23、FFmpeg ./configure编译参数汇总 解压 ffmpeg-snapshot.tar.bz2 之后&…

60.每日一练:回文数(力扣)

目录 问题描述 代码解决以及思想 解法&#xff08;一&#xff09; 知识点 解法&#xff08;二&#xff09; 问题描述 代码解决以及思想 解法&#xff08;一&#xff09; class Solution { public:bool isPalindrome(int x) {string arr to_string(x); // 将整数转换为…

如何自己实现一个丝滑的流程图绘制工具(一)vue如何使用

背景 项目需求突然叫我实现一个类似processOn一样的在线流程图绘制工具。 这可难倒我了&#xff0c;立马去做调研&#xff0c;在github上找了很多个开源的流程图绘制工具&#xff0c; 对比下来我还是选择了 bpmn-js 原因&#xff1a; 1、他的流程图是涉及到业务的&#xff0c…

element ui - el-select获取点击项的整个对象item

1.背景 在使用 el-select 的时候&#xff0c;经常会通过 change 事件来获取当前绑定的 value &#xff0c;即对象中默认的某个 value 值。但在某些特殊情况下&#xff0c;如果想要获取的是点击项的整个对象 item&#xff0c;该怎么做呢&#xff1f; 2.实例 elementUI 中是可…

Vue教程(五):样式绑定——class和style

1、样式代码准备 样式提前准备 <style>.basic{width: 400px;height: 100px;border: 1px solid black;}.happy{border: 4px solid red;background-color: rgba(255, 255, 0, 0.644);background: linear-gradient(30deg, yellow, pink, orange, yellow);}.sad{border: 4px …

Git基础——基本的 Git本地操作

本文涵盖了你在使用Git的绝大多数时间里会用到的所有基础命令。学完之后&#xff0c;你应该能够配置并初始化Git仓库、开始或停止跟踪文件、暂存或者提交更改。我们也会讲授如何让Git忽略某些文件和文件模式&#xff0c;如何简单快速地撤销错误操作&#xff0c;如何浏览项目版本…

【精品】基于VUE3的 电商详情 图片显示模块

效果 组件 <template><div class"goods-imgs"><div class"imgs-show"><img :src"mainImage" alt"大图" /></div><ul class"img-thumbnail"><li v-for"(item, index) in image…

【安装】MongoDB7安装MongoSH命令

MongoDB Shell Download | MongoDB 下载之后 解压 配置环境变量即可 以前使用 mongo命令 改为 mongosh 官方说明 安装mongosh MongoDB 中文手册 | MongoD Manual | 中文操作手册 | MongoDB 最新版 (whaleal.com) 安装 mongosh — MongoDB Shell

Java smslib包开发

上一篇文章我详细介绍RXTXcomm的安装方法和简单代码,如果小伙伴涉及到需要使用手机短信模块完成短信收发需求的话,可以使用到smslib进行开发。 首先还是同样的,将整个smslib包源码导入项目,并且将它所需依赖一起进行导入 导入完成之后,我们就可以对smslib包进行二次开发了 下面…

江西南昌电气机械三维测量仪机械零件3d扫描-CASAIM中科广电

精密机械零部件是指机械设备中起到特定功能的零件&#xff0c;其制造精度要求非常高。这些零部件通常由金属、塑料或陶瓷等材料制成&#xff0c;常见的精密机械零部件包括齿轮、轴承、螺丝、活塞、阀门等。精密机械零部件的制造需要高精度的加工设备和工艺&#xff0c;以确保其…

Ubuntu20.04搭建OpenGL环境(glfw+glad)

Ubuntu20.04搭建OpenGL环境(glfwglad) Linux环境搭建 本文在VMware安装Ubuntu20.04桌面版的环境下搭建OpenGL&#xff0c;按照本文搭建完成后可以执行LearnOpenGL网站上的demo。 关于VMware可自行到VMware Workstation Pro | CN下载 关于Ubuntu20.04桌面版可自行到官网或In…

Docker-Consul

Docker-Consul 一、介绍1.什么是服务注册与发现2.什么是consul3.consul提供的一些关键特性&#xff1a; 二、consul 部署1.环境准备2.consul服务器3.查看集群信息4.通过 http api 获取集群信息 三、registrator服务器1.安装 Gliderlabs/Registrator2.测试服务发现功能是否正常3…

wps 画项目进度甘特图

效果如上 步骤一&#xff1a; 创建excel 表格 步骤二&#xff1a; 选中开始时间和结束时间两列数据&#xff0c;右键设置单元格格式 步骤三&#xff1a; 选择数值&#xff0c;点击确定&#xff0c;将日期转成数值。 步骤四&#xff1a;插入图表 选中任务&#xff0c;开始时间…

ssm+vue毕业论文管理系统源码和论文

ssmvue毕业论文管理系统053 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 高校规模越来越大&#xff0c;学生越来越多&#xff0c;每年都有大批的大学生完成学业。毕业之前&#xff0c;各大高校设立…