【数据结构】 七大排序详解(贰)——冒泡排序、快速排序、归并排序

文章目录

  • ⚽冒泡排序
    • ⚾算法步骤
    • 🎨算法优化
    • 🥎代码实现:
    • 🏀冒泡排序的特性总结
  • 🧭快速排序
    • ⚽算法思路
      • 📌思路一(Hoare版)
      • 📌思路二(挖坑法)
      • 📌思路三(前后指针)
    • 🎨代码实现:
    • 🌳快速排序优化
      • 📌规模较小时的优化
      • 📌三数取中法
    • 🏀快速排序递归实现
      • 🚩代码实现:
    • 🎡快速排序特性总结
  • 🥎归并排序
    • ⚽基本思想
    • 🏀算法步骤
    • 🛫代码实现:
    • 😎递归实现归并排序
    • 🛬归并排序特性总结
    • 🌴海量数据的排序问题
  • 🐱‍🏍排序算法复杂度及稳定性分析
  • ⭕总结

⚽冒泡排序

==冒泡排序(Bubble Sort)==也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。

⚾算法步骤

比较相邻的元素。如果第一个比第二个大,就交换他们两个。

对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

针对所有的元素重复以上的步骤,除了最后一个。

持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

在这里插入图片描述

🎨算法优化

冒泡排序还有一种优化算法,就是立一个 flag,当在一趟序列遍历中元素没有发生交换,则证明该序列已经有序

直接返回就好

🥎代码实现:

    public  int[] bubbleSort(int[] arr) {int[] array = Arrays.copyOf(arr,arr.length);for(int i = 1;i < array.length ; i ++) {Boolean a = true;for(int j = 0; j < array.length - i; j++) {if(array[j] > array[j + 1]) {swap(array,j,j+1);a = false;}}if(a) {return array;}}return array;}private void swap (int[] arr,int m,int n) {int tmp = arr[m];arr[m] = arr[n];arr[n] = tmp;}

🏀冒泡排序的特性总结

  1. 冒泡排序是一种非常容易理解的排序

  2. 时间复杂度:O(N^2)

  3. 空间复杂度:O(1)

  4. 稳定性:稳定

  5. 什么时候最快
    当输入的数据已经是正序时(都已经是正序了,我还要你冒泡排序有何用啊)。

  6. 什么时候最慢
    当输入的数据是反序时(写一个 for 循环反序输出数据不就行了,干嘛要用你冒泡排序呢,我是闲的吗)。

🧭快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

⚽算法思路

📌思路一(Hoare版)

步骤为:

  1. 选取基准值

  2. 从数组右->左找到比基准值小于或等于的值的下标

  3. 从数组右->左找到比基准值大于或等于的值的下标

  4. 交换这两下标的值

  5. 继续执行二操作,直到操作2与操作3相遇

  6. 将基准值放在相遇位置

如下图所示:

在这里插入图片描述

📌思路二(挖坑法)

步骤为:

  1. 选取基准值后,记录下基准值,假设该下标为空,相当于是个“坑”

  2. 从右->左找小于或等于基准值的值,就将该数放入坑中,然后该下标变为新的坑

  3. 从左->右找小于或等于基准值的值,就将该数放入坑中,然后该下标变为新的坑

  4. 回到步骤2继续执行,直到操作2与操作3所找数相同

  5. 将记录下的基准值放回坑里

图示如下:
在这里插入图片描述

📌思路三(前后指针)

步骤及其动图如下:
在这里插入图片描述

🎨代码实现:

    public int[] quickSort(int[] array) {int[] arr = Arrays.copyOf(array,array.length);int left = 0;int right = arr.length - 1;quick(arr,left,right);return arr;}private void quick(int[] array,int begin,int end) {if(begin >= end) {return;}int centre = partition1(array,begin,end);//int centre = partition2(array,begin,end);//int centre = partition3(array,begin,end);quick(array,centre + 1,end);quick(array,begin,centre - 1);}//挖坑法private  int partition1(int[] array,int left,int right) {int tmp = array[left];while (left < right) {while (left< right && array[right] >= tmp) {right--;}array[left] = array[right];while (left< right && array[left] <= tmp) {left++;}array[right] = array[left];}array[left] = tmp;return left;}//Hoare版private  int partition2(int[] array,int left,int right) {int tmp = array[left];int i = left;while (left < right) {while (left< right && array[right] >= tmp) {right--;}while (left< right && array[left] <= tmp) {left++;}swap(array,left,right);}swap(array,left,i);return left;}//前后指针法private int partition3(int[] array,int left,int right) {int prev = left ;int cur = left+1;while (cur <= right) {if(array[cur] < array[left] && array[++prev] != array[cur]) {swap(array,cur,prev);}cur++;}swap(array,prev,left);return prev;}private void swap (int[] arr,int m,int n) {int tmp = arr[m];arr[m] = arr[n];arr[n] = tmp;}

🌳快速排序优化

📌规模较小时的优化

每次递归的时候,数据都是再慢慢变成有序的

当数据量少且趋于有序的时候,我们可以直接使用插入排序进行优化

    private void quick(int[] array,int begin,int end) {if(begin >= end) {return;}if(end - begin < 20) {//插入排序//......return;}int centre = partition1(array,begin,end);//int centre = partition2(array,begin,end);//int centre = partition3(array,begin,end);quick(array,centre + 1,end);quick(array,begin,centre - 1);}

📌三数取中法

如果在选取基数时我们发现如果基数一边总是没有数,代码的执行次数会增加很多

所以我们的解决方法为:
选取数组第一个数、中间的数、和最后一个数,进行比较

三数中间的数作为每次的基数

寻找中间数代码如下:

    private  int midThree(int[] array,int left,int right) {int mid = (left + right) / 2;//6  8if (array[left] < array[right]) {if (array[mid] < array[left]) {return left;} else if (array[mid] > array[right]) {return right;} else {return mid;}} else {//array[left] > array[right]if (array[mid] < array[right]) {return right;} else if (array[mid] > array[left]) {return left;} else {return mid;}}}

使用如下:

    private  int partition1(int[] array,int left,int right) {int tmp = midThree(array,left,right);while (left < right) {while (left< right && array[right] >= tmp) {right--;}array[left] = array[right];while (left< right && array[left] <= tmp) {left++;}array[right] = array[left];}array[left] = tmp;return left;}//Hoare版private  int partition2(int[] array,int left,int right) {int tmp = midThree(array,left,right);int i = left;while (left < right) {while (left< right && array[right] >= tmp) {right--;}while (left< right && array[left] <= tmp) {left++;}swap(array,left,right);}swap(array,left,i);return left;}

🏀快速排序递归实现

实现思路:

  • 建立一个栈

  • 先让一组数据的起点入栈

  • 再让一组数据的终点出栈

  • 在这里插入图片描述

  • 然后两次出栈,分别作为该数据的起点与终点

  • 然后经过我们上面所写的方法进行排序后

  • 再将两组数据进行入栈

  • 在这里插入图片描述

  • 以此循环直到栈为空

🚩代码实现:

    //快速排序递归实现public int[] quickSortPlus(int[] array) {int[] arr = Arrays.copyOf(array,array.length);Deque<Integer> stack = new LinkedList<>();int left = 0;int right = array.length-1;int pivot = 0;stack.push(left);stack.push(right);while (!stack.isEmpty()) {right= stack.pop();left = stack.pop();pivot = partition(arr,left,right);if(pivot > left+1) {stack.push(left);stack.push(pivot-1);}if(pivot < right-1) {stack.push(pivot+1);stack.push(right);}}return arr;}private  int partition(int[] array,int left,int right) {int tmp = array[left];while (left < right) {while (left< right && array[right] >= tmp) {right--;}array[left] = array[right];while (left< right && array[left] <= tmp) {left++;}array[right] = array[left];}array[left] = tmp;return left;}

🎡快速排序特性总结

  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

  2. 时间复杂度:O(N*logN)
    在这里插入图片描述

  3. 空间复杂度:O(logN)

  4. 稳定性:不稳定

🥎归并排序

⚽基本思想

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and
Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:
在这里插入图片描述

🏀算法步骤

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;

  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;

  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;

  4. 重复步骤 3 直到某一指针达到序列尾;

  5. 将另一序列剩下的所有元素直接复制到合并序列尾。

在这里插入图片描述

🛫代码实现:

    public  void mergeSort1(int[] array) {mergeSortFunc(array,0,array.length-1);}private  void mergeSortFunc(int[] array,int left,int right) {if(left >= right) {return;}int mid = (left+right) / 2;mergeSortFunc(array,left,mid);mergeSortFunc(array,mid+1,right);merge(array,left,right,mid);}private  void merge(int[] array,int start,int end,int mid) {int s1 = start;//int e1 = mid;int s2 = mid+1;//int e2 = end;int[] tmp = new int[end-start+1];int k = 0;//tmp数组的下标while (s1 <= mid && s2 <= end) {if(array[s1] <= array[s2]) {tmp[k++] = array[s1++];}else {tmp[k++] = array[s2++];}}while (s1 <= mid) {tmp[k++] = array[s1++];}while (s2 <= end) {tmp[k++] = array[s2++];}for (int i = 0; i < tmp.length; i++) {array[i+start] = tmp[i];}}

😎递归实现归并排序

public static void mergeSort(int[] array) {int gap = 1;while (gap < array.length) {// i += gap * 2 当前gap组的时候,去排序下一组for (int i = 0; i < array.length; 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,right,mid);}//当前为2组有序  下次变成4组有序gap *= 2;}}private  void merge(int[] array,int start,int end,int mid) {int s1 = start;//int e1 = mid;int s2 = mid+1;//int e2 = end;int[] tmp = new int[end-start+1];int k = 0;//tmp数组的下标while (s1 <= mid && s2 <= end) {if(array[s1] <= array[s2]) {tmp[k++] = array[s1++];}else {tmp[k++] = array[s2++];}}while (s1 <= mid) {tmp[k++] = array[s1++];}while (s2 <= end) {tmp[k++] = array[s2++];}for (int i = 0; i < tmp.length; i++) {array[i+start] = tmp[i];}}

🛬归并排序特性总结

  1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。

  2. 时间复杂度:O(N*logN)

  3. 空间复杂度:O(N)

  4. 稳定性:稳定

🌴海量数据的排序问题

外部排序:排序过程需要在磁盘等外部存储进行的排序

前提:内存只有 1G,需要排序的数据有 100G

因为内存中因为无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序

  1. 先把文件切分成 200 份,每个 512 M

  2. 分别对 512 M 排序,因为内存已经可以放的下,所以任意排序方式都可以

  3. 进行 2路归并,同时对 200 份有序文件做归并过程,最终结果就有序了

🐱‍🏍排序算法复杂度及稳定性分析

在这里插入图片描述
在这里插入图片描述

⭕总结

关于《【数据结构】 七大排序详解(贰)——冒泡排序、快速排序、归并排序》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

普通用户使用spark的client无法更新Ranger策略

普通用户使用spark的client无法更新Ranger策略 报错图片&#xff1a; WARN org.apache.ranger.admin.client.RangerAdminRESTClient: Error getting Roles. secureModetrue, usercaojianxiangUCDIPA.VIATRIS.CC (auth:KERBEROS)&#xff0c;responsef"httpStatusCode&quo…

基于SSM的社区管理与服务系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

【JavaScript】JS语法入门到实战

文章目录 一、初识JavaScript1. 什么是JavaScript&#xff1f;2. JavaScript 和 HTML 和 CSS 之间的关系3. JavaScript的运行过程4. JavaScript的组成 二、JavaScript的书写形式三、变量1. 输入输出2. 变量的使用3. 数据类型 四、运算符五、分支和循环语句1. 分支语句2. 循环语…

数据结构之队列的实现(附源码)

目录 一、队列的概念及结构 二、队列的实现 拓展&#xff1a;循环队列 三、初学的队列以及栈和队列结合的练习题 一、队列的概念及结构 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出FIFO(Fi…

后端SpringBoot+前端Vue前后端分离的项目(一)

前言&#xff1a;后端使用SpringBoot框架&#xff0c;前端使用Vue框架&#xff0c;做一个前后端分离的小项目&#xff0c;需求&#xff1a;实现一个表格&#xff0c;具备新增、删除、修改的功能。 目录 一、数据库表的设计 二、后端实现 环境配置 数据处理-增删改查 model…

C++的纯虚函数和抽象类

在C++中,可以将虚函数声明为纯虚函数,语法格式为: virtual 返回值类型 函数名 (函数参数) = 0; 纯虚函数没有函数体,只有函数声明,在虚函数声明的结尾加上=0,表明此函数为纯虚函数。 最后的=0并不表示函数返回值为0,它只起形式上的作用,告诉编译系统“这是纯虚函数”。…

MySQL的概述、版本、安装过程

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 目录 一、MySQL的概述 二、MySQL的版本 三、MySQL的下载与安装 前言 本文将来谈谈MySQL的概述&#xff0c;MySQL的版本&#xff0c;以及它…

Redis 缓存穿透击穿和雪崩

一、说明 Redis 缓存的使用&#xff0c;极大的提升了应用程序的性能和效率&#xff0c;特别是数据查询方面。但同时&#xff0c;它也带来了一些问题。其中&#xff0c;最要害的问题&#xff0c;就是数据的一致性问题&#xff0c;从严格意义上讲&#xff0c;这个问题无解。如果对…

C++11新特性③ | 可变参数模板介绍

目录 1、引言 2、可变参数模板函数 2.1、可变参数模板函数的定义 2.2、参数包的展开 3、可变参数模板类 3.1、继承方式展开参数包 3.2、模板递归和特化方式展开参数包 VC常用功能开发汇总&#xff08;专栏文章列表&#xff0c;欢迎订阅&#xff0c;持续更新...&#xff…

js获得相对路径文件,并上传到服务器

如何通过js获得相对路径文件 已知一个相对路径文件&#xff0c;如何使用js将该文件读取为File格式&#xff0c;最后上传到服务器中呢。 1.最简单的解决方案——fetch 代码 import ./index.scss// js通过相对路径获取文件 function FetchGetLocalFile() {const fetchLocalFile …

Python Opencv实践 - Shi-Tomasi角点检测

参考资料&#xff1a;Harris和Shi-tomasi角点检测笔记&#xff08;详细推导&#xff09;_harris焦点检测_亦枫Leonlew的博客-CSDN博客 cv.goodFeaturesToTrack&#xff1a;Shi-Tomasi角点检测-OpenCV-python_独憩的博客-CSDN博客 import cv2 as cv import numpy as np import …

Python入门学习12

一、Python包 什么是Python包 从物理上看&#xff0c;包就是一个文件夹&#xff0c;在该文件夹下包含了一个 __init__.py 文件&#xff0c;该文件夹可用于包含多个模块文件。从逻辑上看&#xff0c;包的本质依然是模块 包的作用: 当我们的模块文件越来越多时,包可以帮助我们管…

如何处理异步编程中的回调地狱问题?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 解决回调地狱问题的方法⭐使用 Promise⭐使用 async/await⭐ 使用回调函数库⭐模块化⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端…

YOLOV8从零搭建一套目标检测系统(修改model结构必看)附一份工业缺陷检测数据集

目录 1.YOLOV8介绍 2.YOLOV8安装 2.1环境配置 3.数据集准备 1.YOLOV8介绍 Yolov8结构图&#xff1a; YoloV8相对于YoloV5的改进点&#xff1a; Replace the C3 module with the C2f module. Replace the first 6x6 Conv with 3x3 Conv in the Backbone. Delete two Convs …

processflow流程图多人协作预热

前言 在线上办公如火如荼的今天&#xff0c;多人协作功能是每个应用绕不开的门槛。processflow在线流程图&#xff08;前身基于drawio二次开发&#xff09;沉寂两年之久&#xff0c;经过长时间设计开发&#xff0c;调整&#xff0c;最终完成了多人协作的核心模块设计。废话不多…

【SpringSecurity】九、Base64与JWT

文章目录 1、base64编码2、Base64Url3、JWT的产生背景4、JWT介绍5、JWT组成5.1 Header5.2 Payload5.3 Signature 6、JWT的使用方式7、JWT的几个特点 1、base64编码 base64是一种编码方式&#xff0c;不是加密方式。 所谓Base64&#xff0c;就是说选出64个字符&#xff1a;小写…

【办公类-19-03】办公中的思考——Python批量制作word单元格照片和文字(小照片系列)

背景需求&#xff1a; 工会老师求助&#xff1a;如何在word里面插入4*8的框&#xff0c;我怎么也拉不到4*8大小&#xff08;她用的是我WORD 文本框&#xff09; 我一听&#xff0c;这又是要手动反复黏贴“文本框”“照片”“文字”的节奏哦 我问&#xff1a;你要做几个人&…

搭建hadoop集群的常见问题及解决办法

问题一: namenode -format重复初始化 出现问题的原因是重复初始化时会重新生成集群ID&#xff0c;而dn还是原先的集群ID&#xff0c;两者不匹配时无法启动相应的dn进程。 怎么查找问题原因&#xff1a;在logs目录下找到对应节点的.log文件&#xff0c;使用tail -200 文件名来查…

远程工作面试:特殊情况下的面试技巧

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

Web3 solidity编写cancelorder取消订单函数 并梳理讲述逻辑

上文 Web3 solidity订单池操作 中 我们讲述了订单池的基本概念 并手动编写了创建订单的操作 最近的 我们还是先将 ganache 环境起起来 然后 我们打开项目 上文中 我们写了makeOrder创建订单的函数 但是 也带出一个问题 我们创建之后 如果不要了 怎么干掉呀&#xff1f; js中我…