七大排序算法和计数排序

文章目录

  • 一、直接插入排序
  • 二、希尔排序
  • 三、直接选择排序
  • 四、堆排序
  • 五、冒泡排序
  • 六、快速排序
    • 6.1递归实现快速排序
    • 6.2非递归实现快速排序
  • 七、归并排序
    • 7.1递归实现归并排序
    • 7.2非递归实现归并排序
  • 八、计数排序

以下排序以从小到大排序为例

一、直接插入排序

时间复杂度:
最好情况:完全有序的情况 1 2 3 4 5 O(N)
最坏情况:完全逆序的情况 5 4 3 2 1 O(N^2)(相当于等差数列求和)
空间复杂度:O(1)
稳定性:稳定
当所给的数据越有序,直接插入排序越快
有一组基本有序的数据时,用直接插入排序较好

public static void insertSort(int[] array) {for(int i = 1; i < array.length; i++) {int j = i - 1;int tmp = array[i];for(; j >= 0; j--) {//跳出循环有两种情况,1.j<0 2.array[j]<=tmpif(array[j] > tmp) {//如果条件变为array[j]>=tmp,则排序变为不稳定array[j + 1] = array[j];}else {break;}}array[j + 1] = tmp;//每次把tmp插入数组后,都会重新获取i和j的位置}}

二、希尔排序

希尔排序是对直接插入排序的优化,跳跃式的分组可能会将更小的元素尽可能往前方
增量为多少(gap为多少),则被分为多少组
时间复杂度:N^1.3 ~ N^1.5
空间复杂度:O(1)
稳定性:不稳定

public static void shellSort(int[] array) {int gap = array.length;//增量为多少(gap为多少),则被分为多少组while(gap > 1) {//里面已经包括了排序gap为1的情况gap /= 2;shell(array, gap);}}private static void shell(int[] array, int gap) {for(int i = gap; i < array.length; i++) {//i+=gap也行,因为gap最后会变为1int j = i - gap;int tmp = array[i];for(; j >= 0; j -= gap) {//跳出循环有两种情况,1.j<0 2.array[j]<=tmpif(array[j] > tmp) {//如果条件变为array[j]>=tmp,则排序变为不稳定array[j + gap] = array[j];}else {break;}}array[j + gap] = tmp;//每次把tmp插入数组后,都会重新获取i和j的位置}}

三、直接选择排序

时间复杂度:不管情况是好还是坏,下面两种写法都是O(N^2)(相当于等差数列求和)
空间复杂度:O(1)
稳定性:不稳定

public static void selectSort1(int[] array){for(int i = 0; i < array.length; i++) {int minIndex = i;for(int j = i + 1; j < array.length; j++) {if(array[j] < array[minIndex]) {minIndex = j;//在所有的j下标元素中找比array[minIndex]还要小的元素的下标}}swap(array, i, minIndex);}}private 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) {//与写法一相比将写法一最外层的for循环改为了while循环int minIndex = left;int maxIndex = left;//maxIndex一定是left,因为下面的j下标是从left+1开始从前往后遍历for(int i = left + 1; i <= right; i++) {if(array[i] < array[minIndex]) {minIndex = i;//在所有的j下标元素中找比array[minIndex]还要小的元素的下标}if(array[i] > array[maxIndex]) {maxIndex = i;//在所有的j下标元素中找比array[maxIndex]还要大的元素的下标}}swap(array, left, minIndex);//当最大值原来刚好在最小值的位置(left位置)时,则上一步已经将最小值与left交换,此时最大值在原来最小值的位置,if(maxIndex == left) {//所以要做这一步操作maxIndex = minIndex;}swap(array, right, maxIndex);left--;right++;}}

四、堆排序

时间复杂度:O(N)(创建大根堆)+ O(NlogN) (堆的每个节点进行向下调整) 即O(NlogN)
空间复杂度:O(1)
稳定性:不稳定
数据量非常大的时候,堆排序一定比希尔排序快,因为希尔排序的时间复杂度为N^1.3 ~ N^1.4,堆排是对数希尔排是指数

public static void heapSort(int[] array){creatBigHeap(array);int end = array.length - 1;while(end > 0) {swap(array, 0, end);//因为是大堆,堆顶的元素最大,将堆顶元素与队尾元素交换,使队尾元素变成队内最大元素shiftDown(array, 0, end);//在这里end=array.length-1,而在shiftDown中end代表数组的总长度,end--;                          //相当于去掉队尾元素再进行向下调整}}private static void creatBigHeap(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 = 2 * parent + 1;while(child < end) {//因为end传的是array.length,所以条件用<而不用<=if(child + 1 < end && array[child] < array[child + 1]) {child++;}if(array[child] > array[parent]) {swap(array, child, parent);parent = child;child = 2 * parent + 1;}else {break;}}}

五、冒泡排序

时间复杂度:O(N^2) 加了优化之后,最好的情况(只比较一趟)则是O(N)
空间复杂度:O(1)
稳定性:稳定

public static void bubbleSort(int[] array){for (int i = 0; i < array.length - 1; i++) {boolean flg = false;for (int j = 0; j < array.length - 1 - i; j++) {if(array[j] > array[j + 1]) {swap(array, j, j + 1);flg = true;}}if(!flg) {return;}}}

六、快速排序

快速排序(相当于以基准为根创建二叉树)
时间复杂度:
最好情况:O(N*logN) 满二叉树/完全二叉树
最坏情况:O(N^2)(相当于等差数列求和) 单分支的树
空间复杂度:
最好情况:O(logN) 满二叉树/完全二叉树的高度
最坏情况:O(N) 单分支的树创建N个节点
稳定性:不稳定
三种求基准的方法:Hoare法,挖坑法,前后指针法

6.1递归实现快速排序

在这里递归实现的快速排序做了两个优化,第一个优化是使用三数取中法求原始基准,使创建出来的树更像满二叉树,降低了树的高度,降低了空间复杂度
在这里插入图片描述
第二个优化是递归到一定程度时对每个区间使用插入排序,因为区间越来越小且区间的内容越来越有序,这时这部分区间可以用插入排序以减少递归次数

public static void quickSort(int[] array){quickSortF(array, 0, array.length - 1);//参数right传的是array.length-1}private static void quickSortF(int[] array, int left, int right) {if(left >= right) return;//这是递归的结束条件,有=,因为left=right时不用再创建节点了,这个节点已经有序//递归到后面的较小区间时用插入法if(right - left + 1 <= 7) {//随着快速排序的进行,整个数据正在趋于有序,当区间越来越小且区间的内容越来越有序时,insertSort1(array, left, right);//这部分区间可以用插入排序,这样做可以减少递归的次数return;}//三数取中法,降低了二叉树的高度,降低了空间复杂度,使空间复杂度变为O(logn)int midIndex = midOfTree(array, left, right);swap(array, midIndex, left);//交换完之后保证left下标是三个数中中间大的数字int pivot = partition2(array, left, right);//找到一次基准相当于排好了当前基准这个元素在数组中的顺序,找到基准后,基准左边都是比基准小的,基准右边都是比基准大的//先递归左边,递归完左边再递归右边quickSortF(array, left, pivot - 1);quickSortF(array, pivot + 1, right);}//插入法private static void insertSort1(int[] array, int left, int right) {for(int i = left + 1; i <= right; i++) {int j = i - 1;int tmp = array[i];for(; j >= left; j--) {//跳出循环有两种情况,1.j<0 2.array[j]<=tmpif(array[j] > tmp) {//如果条件变为array[j]>=tmp,则排序变为不稳定array[j + 1] = array[j];}else {break;}}array[j + 1] = tmp;//每次把tmp插入数组后,都会重新获取i和j的位置}}//在三数中找到中间大小数的下标private static int midOfTree(int[] array, int left, int right) {int mid = (left + right) / 2;if(array[left] < array[right]) {if(array[mid] < array[left]) {return left;}else if(array[mid] > array[right]) {return right;}else {return mid;}}else {if(array[mid] > array[left]) {return left;}else if(array[mid] < array[right]) {return right;}else {return mid;}}}//三种找基准的方法,在排序的过程中序列可能会不一样,但最终排好序的结果一样,建议优先使用挖坑法,right参数传的都是array.length-1//Hoare法找基准private static int partition1(int[] array, int left, int right) {int key = array[left];int i = left;//将基准的下标保存到i中,因为后面要用left与right相交位置的元素与基准进行交换,这里的交换函数只传下标while(left < right) {while (left < right && array[right] >= key) {//left<right这个条件很重要,因为right不断--,此时再判断array[right]>=key时array[right]有可能会越界right--;//先走right,因为一开始基准为left,先走right,left和right相遇的地方才能是比基准小的,才能把比基准小的与基准交换放在基准前}while (left < right && array[left] <= key) {//array[left]<=key或上面array[right]>=key的=一定要有,否则可能会进入死循环(如当left和right的值相同时)left++;}swap(array, left, right);}swap(array, left, i);return left;}//挖坑法找基准private static int partition2(int[] array, int left, int right) {int key = array[left];while(left < right) {while (left < right && array[right] >= key) {right--;}array[left] = array[right];while (left < right && array[left] <= key) {left++;}array[right] = array[left];}array[left] = key;return left;}//前后指针法找基准private static 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]) {//cur在前面找比基准小的,prev保持在cur找到比基准小的之后要跟prev交换的那个位置,prev是前置++swap(array, prev, cur);//代码走到这说明cur和prev拉开了距离且cur<left,prev>=left}cur++;//array[cur]>=array[left]时cur直接往前走,与prev拉开距离}swap(array, prev, left);return prev;}

6.2非递归实现快速排序

public static void quickSortNor(int[] array) {Stack<Integer> stack = new Stack<>();int left = 0;int right = array.length - 1;int piovt = partition2(array, left, right);if(piovt - 1 > left) {stack.push(left);//往栈上先放left后放right,则出栈时先出right后出leftstack.push(piovt - 1);}if(piovt + 1 < right) {stack.push(piovt + 1);stack.push(right);}while(!stack.isEmpty()) {//因为这里出栈时先出right后出left,根据出出来的right和left找新的基准,所以相当于先递归二叉树右边,再递归二叉树左边right = stack.pop();left = stack.pop();piovt = partition2(array, left, right);if(piovt - 1 > left) {stack.push(left);stack.push(piovt - 1);}if(piovt + 1 < right) {stack.push(piovt + 1);stack.push(right);}}}

七、归并排序

时间复杂度:O(N*logN) 归并排序在合并过程中每层都要遍历N次,一共logN层
空间复杂度:O(N) 归并排序在最后合并的时候要额外申请与原来数组一模一样大小的数组
归并排序的缺点是空间复杂度过大
稳定性:稳定
原始分开的区间从0下标开始和原始分开的区间不从0下标开始的合并过程:
在这里插入图片描述
在这里插入图片描述

7.1递归实现归并排序

public static void mergeSort(int[] array) {mergeSort(array, 0, array.length - 1);//参数right传的是array.length-1}private static void mergeSort(int[] array, int left, int right) {if(left >= right) return;int mid = (left + right) / 2;//分裂左边mergeSort(array, left, mid);//分裂右边mergeSort(array, mid + 1, right);//合并,在合并的过程中进行了排序merge(array, left, right, mid);}private static void merge(int[] array, int left, int right, int mid) {int s1 = left;int s2 = mid + 1;int[] tmpArr = new int[right - left + 1];//申请一个新的数组,大小为right-left+1int k = 0;while(s1 <= mid && s2 <= right) {//满足这个循环条件说明s1~mid和s2~right这两个区间都同时有数据if(array[s2] < array[s1]) {//若条件改为array[s2]<=array[s1]则排序变为不稳定tmpArr[k++] = array[s2++];}else {tmpArr[k++] = array[s1++];}}while(s1 <= mid) {tmpArr[k++] = array[s1++];}while(s2 <= right) {tmpArr[k++] = array[s2++];}for (int i = 0; i < tmpArr.length; i++) {array[i + left] = tmpArr[i];//将tmpArr数组拷回原来数组时,赋给array[i+left],因为原来数组的left有可能不是0}}

7.2非递归实现归并排序

在合并之前分的组数不断减少,每组元素不断增多,一个一个为一组(gap为1),再到两个两个为一组(gap为2),如此类推
在这里插入图片描述
合并之前mid和right有可能会越界,此时要做出调整
在这里插入图片描述

public static void mergeSortNor(int[] array) {int gap = 1;while(gap < array.length) {for (int i = 0; i < array.length; i += 2*gap) {int left = i;int mid = left + gap - 1;int right = mid + gap;if(mid >= array.length) {//mid有可能会越界,越界时要做出调整mid = array.length - 1;}if(right >= array.length) {//right有可能会越界,越界时要做出调整right = array.length - 1;}merge(array, left, right, mid);}gap *= 2;}}

八、计数排序

时间复杂度:O(2N+数据范围) 即O(MAX(N,数据范围))
空间复杂度:O(数据范围)
稳定性:稳定,但以下代码实现的计数排序是不稳定的
计数排序适合排序某个区间内且集中的数据

   public static void countSort(int[] array) {int minVal = array[0];int maxVal = array[0];//求数组中的最大值和最小值for (int i = 0; i < array.length; i++) {if(array[i] < minVal) {minVal = array[i];}if(array[i] > maxVal) {maxVal = array[i];}}int[] count = new int[maxVal - minVal + 1];//依据最大值和最小值来确定计数数组的大小//遍历原来的数组进行计数for (int i = 0; i < array.length; i++) {count[array[i] - minVal]++;//计数数组的下标相当于要排序数组的元素内容}//遍历count,把当前元素写回arrayint index = 0;for (int i = 0; i < count.length; i++) {while(count[i] != 0) {array[index] = i + minVal;index++;count[i]--;}}}

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

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

相关文章

文章审核之敏感词过滤

技术选型 DFA实现原理 DFA全称为&#xff1a;Deterministic Finite Automaton,即确定有穷自动机。 存储&#xff1a;一次性的把所有的敏感词存储到了多个map中&#xff0c;就是下图表示这种结构 敏感词&#xff1a;冰毒、大麻、大坏蛋 工具类 最下面的main方法是测试用的&a…

Java版本电子招标采购系统源代码—企业战略布局下的采购寻源

智慧寻源 多策略、多场景寻源&#xff0c;多种看板让寻源过程全程可监控&#xff0c;根据不同采购场景&#xff0c;采取不同寻源策略&#xff0c; 实现采购寻源线上化管控&#xff1b;同时支持公域和私域寻源。 询价比价 全程线上询比价&#xff0c;信息公开透明&#xff0c;可…

微信小程序-地图上的图标计算旋转值朝向经纬度计算

废话不多说&#xff0c;开整 // 参数为寄件人经纬度和收件人经纬度 // 根据寄收件人经纬度弧度π进行rotate旋转计算 const getRotate (po1, po2) > {if (!(po1 && po2)) return 0const lng_a po1.longitudeconst lat_a po1.latitudeconst lng_b po2.longitud…

MySQL使用

目录 1 MySQL的登录 1.1 服务的启动和终止 1.2 自带客户端的登录与退出 2 MySQL演示使用 2.1 MySQL的使用演示 2.2 MySQL的编码设置 1 MySQL的登录 1.1 服务的启动和终止 MySQL安装完毕以后&#xff0c;需要启动服务器进程&#xff0c;不然客户端无法连接数据库。 在前面…

vue-cli项目中,使用webpack-bundle-analyzer进行模块分析,查看各个模块的体积,方便后期代码优化

一、安装 npm install --save-dev webpack-bundle-analyzer 二、在vue.config.js中配置 const BundleAnalyzerPlugin require(webpack-bundle-analyzer).BundleAnalyzerPlugin plugins: [new BundleAnalyzerPlugin({analyzerMode: server,analyzerHost: 127.0.0.1,analyze…

Word2Vec实现文本识别分类

深度学习训练营之使用Word2Vec实现文本识别分类 原文链接环境介绍前言前置工作设置GPU数据查看构建数据迭代器 Word2Vec的调用生成数据批次和迭代器模型训练初始化拆分数据集并进行训练 预测 原文链接 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&…

探析国内数字孪生引擎技术现状

在数字孪生软件来发中&#xff0c;渲染引擎是一个关键点&#xff0c;国内大多数字孪生平台引擎通常使用的是自研的渲染引擎或者采用开源的渲染引擎。下面通过一些常见的渲染引擎在国内数字孪生引擎中的应用带大家了解数字孪生软件开发的方式。 自研渲染引擎&#xff1a;许多数…

HTTPS安全套接字层超文本传输协议

HTTPS安全套接字层超文本传输协议 HTTPS简介HTTPS和HTTP的主要区别客户端在使用HTTPS方式与Web服务器通信时的步骤SSL/TLS协议的加密&#xff08;握手&#xff09;过程为什么数据传输阶段使用对称加密HTTPS 的优点HTTPS 的缺点HTTPS 的优化证书优化会话复用 HTTPS简介 HTTP协议…

文件包含漏洞利用思路

简介 通过PHP函数引入文件时&#xff0c;传入的文件名没有经过合理的验证&#xff0c;从而操作了预想之外的文件&#xff0c;导致意外的文件泄漏甚至恶意代码注入。 常见的文件包含函数 php中常见的文件包含函数有以下四种&#xff1a; include()require()include_once()re…

苍穹外卖day05——Redis(被病毒入侵)+店铺营业状态设置

Redis被病毒入侵了 数据删光光然后只剩这四个玩意&#xff0c;乱下东西乱删东西&#xff0c;还好是docker部署&#xff0c;不然就寄了。 在服务器上部署redis记得一定要设置密码&#xff0c;不然被人扫肉鸡注入病毒整个服务器给你崩掉。 使用配置类的方式搭建相关程序 配置数…

实现简单Spring基于XML的配置程序

定义一个容器&#xff0c;使用ConcurrentHashMap 做为单例对象的容器 先解析beans.xml得到第一个bean对象的信息&#xff0c;id&#xff0c;class&#xff0c;属性和属性值使用反射生成对象&#xff0c;并赋值将创建好的bean对象放入到singletonObjects集合中提供getBean(id)方…

【Redis】剖析RDB和AOF持久化原理

文章目录 前言1、AOF日志1.1、概述1.2、日志文件1.3、写回策略1.4、策略实现原理1.5、重写机制1.6、AOF 后台重写1.6.1、介绍1.6.2、实现原理 1.7、优缺点 2、RDB快照2.1、概述2.2、实现方式2.3、实现原理2.4、极端情况2.5、优缺点 3、混合体实现4、大Key问题4.1、何为大key4.2…

profinet 调试记录

一、 树莓派运行codesys runtime 1. 用户名称要以 root 登录 若是普通用户&#xff0c;会提示&#xff1a;脚本必须以 root 身份运行 2. codesys报错&#xff1a; 在树莓派config.txt文件添加&#xff1a;arm_64bit0 3. 扫描设备需开启PLC 图标变红&#xff0c;则开启成…

【MATLAB第58期】基于MATLAB的PCA-Kmeans、PCA-LVQ与BP神经网络分类预测模型对比

【MATLAB第58期】基于MATLAB的PCA-Kmeans、PCA-LVQ与BP神经网络分类预测模型对比 一、数据介绍 基于UCI葡萄酒数据集进行葡萄酒分类及产地预测 共包含178组样本数据&#xff0c;来源于三个葡萄酒产地&#xff0c;每组数据包含产地标签及13种化学元素含量&#xff0c;即已知类…

STM32H5开发(1)----总览

STM32H5开发----1.总览 概述样品申请STM32H5-2MB 框图产品列表STM32H5-2MB 框图STM32H5-128KB框图功能对比STM32H5-128KB vs H5-2MB组员对比STM32H5 亮点 概述 STM32H5系列微控制器是意法半导体公司推出的一款高性能MCU, CortexM33内核的微控制器产品。 他和STM32F2、F4、F7、…

论文精度系列之详解图神经网络

论文地址:A Gentle Introduction to Graph Neural Networks 翻译:图表就在我们身边;现实世界的对象通常根据它们与其他事物的连接来定义。一组对象以及它们之间的连接自然地表示为图形。十多年来&#xff0c;研究人员已经开发了对图数据进行操作的神经网络&#xff08;称为图神…

CentOS 7.9 安装 mydumper(RPM方式)

链接&#xff1a;https://pan.baidu.com/s/1sGhtiKPOmJw1xj0zv-djkA?pwdtaoz 码&#xff1a;taoz 开始正文啦&#xff1a; rpm -ivh mydumper-0.14.5-3-zstd.el7.x86_64.rpm 问题如下&#xff1a; 解决&#xff1a; yum -y install epel-release yum install -y libzstd …

zabbix安装Grafana

一、web访问 https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.6.1-1.x86_64.rpm [rootserver ~] yum localinstall -y grafana-4.6.1-1.x86_64.rpm //yum方式安装本地rpm并自动解决依赖关系 [rootserver ~] grafana-cli plugins install alexanderzob…

利用 trait 实现多态

我在书上看到基于 std::io::Write 的示例&#xff0c;它是一个 trait 类型&#xff0c;内部声明了一些方法。和 go 语言不同&#xff0c;rust 中类型必须明确实现 trait 类型&#xff0c;而 go 语言属于 duck 模式。 std::io::Write下面的例子中调用 write_all 方式来演示&…

国标GB28181视频监控平台EasyGBS无法播放,抓包返回ICMP的排查过程

国标GB28181视频平台EasyGBS是基于国标GB/T28181协议的行业内安防视频流媒体能力平台&#xff0c;可实现的视频功能包括&#xff1a;实时监控直播、录像、检索与回看、语音对讲、云存储、告警、平台级联等功能。国标GB28181视频监控平台部署简单、可拓展性强&#xff0c;支持将…