常见的七种排序

目录

一、插入排序

1、直接插入排序

2、希尔排序(缩小增量排序)

二、选择排序 

3、直接选择排序

 4、堆排序

三、交换排序

5、冒泡排序

6、快速排序

四、归并排序 

7、归并排序

五、总结


一、插入排序

1、直接插入排序

思路:

i 用来遍历数组,拿到一个就放进 tmp,

j 从 i 的前一个开始,每次都和 tmp里的值进行比较,若比tmp的值大,j 的值给到 j+1,j--

直到 j 的值比tmp小,或者 j 减到 <0,循环结束,tmp 的值给到 j+1

  • 时间复杂度:最坏情况下,逆序,O(n^2);最好情况下,有序,O(n)
  • 空间复杂度:O(1)
  • 稳定性稳定
  • 特点:当数据量不多,且基本上趋于有序时,使用直接插入排序很快,趋于O(n)
public class InsertSort {public 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{break;}}array[j+1] = tmp;}}
}

 

2、希尔排序(缩小增量排序)

  • 时间复杂度:O(n^1.3)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
  • 特点:是对直接插入排序的优化,在最后进行直接插入排序之前,增加了预排序。
/*希尔排序(缩小增量排序):gap每次除2
*/
public class ShellSort {public void shellSort(int[] array){int gap = array.length;while(gap > 1){gap /= 2;shell(array,gap);}}public 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;}}
}

二、选择排序 

3、直接选择排序

思路:(走一遍,找到一个最小值)

i 用来遍历数组,拿到一个下标就放进 mIndex

j 从 i 的后一个开始,遍历数组,遇到比 mIndex里的值 小的就更新 mIndex

这一轮遍历完,mIndex里存的就是最小值的下标,把 i 和 mIndex 下标的元素 交换,i++

优化后的思路:(用left 和 right 来遍历数组,走一遍能找到一个最小值和一个最大值)

left 和 right 分别指向 数组的左右两边,minIndex 和 maxIndex 的初始值是 left

j 从 left 的后一个开始遍历,遍历数组 [left+1,right],遇到比 minIndex里的值 小的就更新 minIndex,遇到比 maxIndex里的值 大的就更新 maxIndex

这一轮遍历完,minIndex里存的就是最小值的下标,maxIndex里存的就是最大值的下标,然后把 left 和 minIndex 下标的元素交换,把 right 和 maxIndex 下标的元素交换,left++,right--,但如果 maxIndex 刚好是left,那么最大值就会被换到 minIndex 下标的位置,就得先更新一下 maxIndex,让 maxIndex = minIndex

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

public class SelectSort {public void selectSort(int[] array){for (int i = 0; i < array.length; i++) {int mIndex = i;for (int j = i+1; j < array.length; j++) {if(array[j] < array[mIndex]){mIndex = j;}}//走到这,mIndex里存的是[i,array.length)中最小值的下标int tmp = array[i];array[i] = array[mIndex];array[mIndex] = tmp;}}
}

优化后:

 public void select(int[] array){int left = 0;int right = array.length-1;while(left < right){int minIndex = left;int maxIndex = left;for (int j = left+1; j <= right; j++) {if(array[j] < array[minIndex]){minIndex = j;}if(array[j] > array[maxIndex]){maxIndex = j;}}//走到这,minIndex存的是最小值的下标,maxIndex存的是最大值的下标swap(array, left, minIndex);//如果最大值的下标是leftif(maxIndex == left){maxIndex = minIndex;}swap(array, right, maxIndex);left++;right--;}}public void swap(int[] array,int x,int y){int tmp = array[x];array[x] = array[y];array[y] = tmp;}

 4、堆排序

  • 时间复杂度:O(n*log n)  
  • 空间复杂度:O(1)
  • 稳定性:不稳定
  • 堆排的时间复杂度:建大根堆的时间复杂度+排序的时间复杂度,建大根堆的时间复杂度:O(n),排序的时间复杂度:O(n*log n) —— 每次shiftDown 0的时间复杂度是 log n,要 n-1 次,所以堆排的时间复杂度:O(n)+O(n*log n) ≈ O(n*log n)
public class HeapSort {public void heapSort(int[] array){//首先,建一个大根堆createBigHeap(array);//然后排序int end = array.length-1;while(end > 0){swap(array,0,end);shiftDown(array,0,end);end--;}}public void createBigHeap(int[] array){for (int parent = (array.length-1-1)/2; parent >= 0; parent--) {//每个子树都需要向下调整成大根堆shiftDown(array,parent,array.length);}}public void shiftDown(int[] array,int parent,int end){int child = 2*parent+1;while(child < end){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;}}}public void swap(int[] array,int x,int y){int tmp = array[x];array[x] = array[y];array[y] = tmp;}
}

三、交换排序

5、冒泡排序

思路:

相邻的两个元素进行比较,i 是趟数,j 是每一趟要比较的次数,每一趟都会把一个最大值放到后面。

  • 时间复杂度:(不考虑优化)O(n^2),如果考虑优化的话,最好情况下可以达到O(n)
  • 空间复杂度:O(1)
  • 稳定性 稳定
public class BubbleSort {public void bubbleSort(int[] array){//趟数for (int i = 0; i < array.length-1; i++) {boolean flag = true;//1趟for (int j = 0; j < array.length-1-i; j++) {if(array[j] > array[j+1]){swap(array,j,j+1);flag = false;}}//如果flag还是true,说明这一趟中没有进入过if语句进行交换,说明是元素是有序的if(flag){break;}}}public void swap(int[] array,int x,int y){int tmp = array[x];array[x] = array[y];array[y] = tmp;}
}

6、快速排序

  • 时间复杂度:O(n*logn)
  • 空间复杂度:O(logn)
  • 稳定性:不稳定
  • 时间复杂度:每层遍历的都是n,要遍历树的高度层,树的高度是logn,所以时间复杂度是nlogn;空间复杂度:需要额外开辟的空间就是存pivot这个基准需要的空间,由于当左边递归完去递归右边时,左边给基准开辟的空间就会被回收,所以需要额外给pivot开辟的空间就是树的高度,所以空间复杂度是logn
  • 上述快排的时间复杂度和空间复杂度不是最坏的,当数据是顺序或逆序时,二叉树只有左树或只有右树,达到最坏,此时时间复杂度是O(n^2),空间复杂度是O(n)

但我们可以优化代码,不让它出现只有左树或只有右树的情况。

1、优化方法一:(解决划分不均匀的问题)

定义一个mid = (start+end)/2

在找基准之前,判断 start,end,mid,三个下标对应的值,谁是中间的那个,返回下标。

然后,与start下标进行交换。尽量解决划分不均匀的问题 

2、优化方法二:(减少后几层的递归,解决效率问题)

递归到小的子区间时,可以考虑使用插入排序。

我们发现,后几层占了整棵树的大部分结点,递归的次数最多发生在后面。所以,我们可以减少后几层的递归来解决效率问题。递归区间很小的时候,我们就不递归了,使用直接插入排序。(这时数据页越来越有序了,使用直接插入排序的时间复杂度趋近O(n),是很快的)

(1)Hoare 法:

找基准:

把left的下标记录下来为i,并把left下标对应的值放进tmp,

从右边找到一个比tmp小的,从左边找到一个比tmp大的,然后交换。这个过程是个循环,循环的条件是 left < right,一旦left和right相等了,就会出循环,此时left和right下标就是基准,交换i和基准对应的值。到这里,基准的左边都是比它小的(或等于它的),基准的右边都是比它大的(或等于它的)

public class QuickSort {public void quick(int[] array,int start,int end){if(start >= end){return;}// end-start+1 是 [start,end]这个区间元素的个数if(end-start+1 <= 15){//对 start 和 end 区间范围内使用插入排序insertSort(array,start,end);return;}//找三个值中中间值的下标int mid = findMidOfIndex(array,start,end);swap(array,mid,start);//找基准int pivot = partition(array,start,end);//pivot 就是基准,然后分而治之quick(array,start,pivot-1);quick(array,pivot+1,end);}public void quickSort(int[] array){quick(array,0,array.length-1);}public void insertSort(int[] array,int start,int end){for (int i = start+1; i <= end; i++) {int tmp = array[i];int j = i-1;for (; j >= start; j--) {if(array[j] > tmp){array[j+1] = array[j];}else{break;}}array[j+1] = tmp;}}private int findMidOfIndex(int[] array, int start, int end) {int mid = (start+end)/2;if(array[start] < array[end]){if(array[mid] < array[start]){return start;}else if(array[mid] > array[end]){return end;}else{return mid;}}else{if(array[mid] > array[start]){return start;}else if(array[mid] < array[end]){return end;}else{return mid;}}}public int partition(int[] array,int left,int right){//把left下标记录下来,并把值放进tmp,后面都和tmp进行比较int i = left;int tmp = array[left];// left < right 不能是 <= ,当 left == right 时,说明这一趟走完了,基准的下标找到了while(left < right){/** 要先从右边找到一个比tmp小的,再从左边找到一个比tmp大的,不能反过来* 因为如果反过来了,就可能会出现我从左边找到了一个比tmp大的后,开始从右边找比tmp小的,* 但是还没有找到left和right就相等了。此时,left和right下标对应的值就是比tmp大的值* 出循环后, swap(array,i,left) 就会将大的值换到基准前面去。所以不能反过来。* 按照先从右边找一个比tmp小的的方式,我们会先找到一个比tmp小的,即使还没找到比tmp大的就相遇了,* left和right下标对应的值也是比tmp小的值,交换后会将小的值放到前面。* 所以,一定要先从右边找比tmp小的值!!!*///从右面找到一个比tmp小的while(left < right && array[right] >= tmp){right--;}//从左面找到一个比tmp大的while(left < right && array[left] <= tmp){left++;}//从到这,left下标里存的是比tmp大的值,right下标里存的是比tmp小的值swap(array,left,right);}swap(array,i,left);return left;}public void swap(int[] array,int x,int y){int tmp = array[x];array[x] = array[y];array[y] = tmp;}}

(2)挖坑法: (做题优先使用挖坑法)

找基准:

把left下标对应的值放进tmp,

从右边找到一个比tmp小的(下标是right),放进left下标的坑;再从左边找到一个比tmp大的(下标是left),放进right下标的坑。这个过程是个循环,循环的条件是 left<right,直到left和right相等,退出循环,此时left和right就是基准。将tmp放进基准的这个坑里。到这里,基准的左边都是比它小的(或等于它的),基准的右边都是比它大的(或等于它的)

public class QuickSort2 {public void quickSort(int[] array){quick(array,0,array.length-1);}private void quick(int[] array, int start, int end) {//先找基准,然后找基准左边的基准,然后找基准右边的基准if(start >= end){return;}// end-start+1 是 [start,end]这个区间元素的个数if(end-start+1 <= 15){//对 start 和 end 区间范围内使用插入排序insertSort(array,start,end);return;}//找三个值中中间值的下标int mid = findMidOfIndex(array,start,end);swap(array,mid,start);//找基准int pivot = partition(array,start,end);quick(array,start,pivot-1);quick(array,pivot+1,end);}public void insertSort(int[] array,int start,int end){for (int i = start+1; i <= end; i++) {int tmp = array[i];int j = i-1;for (; j >= start; j--) {if(array[j] > tmp){array[j+1] = array[j];}else{break;}}array[j+1] = tmp;}}private int findMidOfIndex(int[] array, int start, int end) {int mid = (start+end)/2;if(array[start] < array[end]){if(array[mid] < array[start]){return start;}else if(array[mid] > array[end]){return end;}else{return mid;}}else{if(array[mid] > array[start]){return start;}else if(array[mid] < array[end]){return end;}else{return mid;}}}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;}private void swap(int[] array, int x, int y) {int tmp = array[x];array[x] = array[y];array[y] = tmp;}
}

四、归并排序 

7、归并排序

思路:

先分解,再合并

分解到一个一个的元素(递),然后合并(归)

主要逻辑就是,将两个有序的数组合并成一个有序的数组。 

  • 时间复杂度:O(n*logn)
  • 空间复杂度:O(n)
  • 稳定性:稳定
public class MergeSort {public void mergeSort(int[] array){int start = 0;int end = array.length-1;int mid = (start+end)/2;mergeSortChild(array,start,mid,end);}public void mergeSortChild(int[] array,int start,int mid,int end){if(start == end){return;}int s1 = 0;int e1 = mid;int s2 = mid+1;int e2 = end;//分解:分解到start==end,即只有一个元素mergeSortChild(array,s1,(s1+e1)/2,e1);mergeSortChild(array,s2,(s2+e2)/2,e2);//合并merge(array,s1,e1,s2,e2);}//把两个有序数组合成一个有序的数组public void merge(int[] array,int s1,int e1,int s2,int e2){int s = s1;int[] tmpArr = new int[e2-s1+1];int k = 0;while(s1<=e1 && s2<=e2){if(array[s1] < array[s2]){tmpArr[k++] = array[s1++];}else{tmpArr[k++] = array[s2++];}}while(s1 <= e1){tmpArr[k++] = array[s1++];}while(s2 <= e2){tmpArr[k++] = array[s2++];}for (int i = 0; i < k; i++) {array[s+i] = tmpArr[i];}}
}

五、总结

排序方法时间复杂度空间复杂度稳定性
直接插入排序

O(n^2)

最好情况下:O(n)

O(1)稳定
希尔排序O(n^1.3)O(1)不稳定
直接选择排序O(n^2)O(1)不稳定
堆排序O(n*logn)O(1)不稳定
冒泡排序

O(n^2)

最好情况下:O(n)

O(1)稳定
快速排序

O(n*logn)

最坏情况下:O(n^2)

O(logn)

最坏情况下:O(n)

不稳定
归并排序O(n*logn)O(n)稳定

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

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

相关文章

文本溢出体验进阶:CSS 技巧实现单行/多行隐藏展示以及实际场景应用,确保内容可读性和布局整洁性

CSS文本溢出隐藏是一种常见的场景&#xff0c;它广泛应用于各种网页设计中&#xff0c;旨在确保内容的可读性和布局的整洁性&#xff0c;特别是在空间有限或需要适应不同屏幕尺寸的情况下。 一、文本溢出隐藏并显示省略号 1、单行文本溢出隐藏并显示省略号 对于单行文本&…

paho-mqtt 库揭秘

文章目录 **paho-mqtt 库揭秘**第一部分&#xff1a;背景介绍第二部分&#xff1a;paho-mqtt 是什么&#xff1f;第三部分&#xff1a;如何安装这个库&#xff1f;第四部分&#xff1a;库函数使用方法第五部分&#xff1a;场景应用第六部分&#xff1a;常见Bug及解决方案第七部…

Swift-25-普通函数、闭包函数与Lamda表达式编程

函数 语法定义 先来看下swift中函数的定义&#xff0c;函数用关键字func来指定&#xff0c;语法相对复杂一点&#xff0c;主要有下列4种基本情况&#xff0c;还有比较复杂的&#xff0c;会在后续详细讲解。 无参函数定义 有参函数定义 一个简单的函数和函数调用示例如下&…

分类预测 | Matlab实现SCSO-SVM沙猫群优化算法优化支持向量机多特征分类预测

分类预测 | Matlab实现SCSO-SVM沙猫群优化算法优化支持向量机多特征分类预测 目录 分类预测 | Matlab实现SCSO-SVM沙猫群优化算法优化支持向量机多特征分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现SCSO-SVM沙猫群优化算法优化支持向量机多特征分类…

A1322 电烙铁发热芯热电偶温度与电压数据

就是这种四根线比较细的发热芯&#xff1a; 两根红色线是发热丝&#xff0c;另外两根是热电偶线&#xff0c;透明线是正极&#xff0c;不能搞错了。 非常粗略的测了一下&#xff0c;根本没有考虑误差。拿万用表直接测量热电偶的输出电压&#xff1b;用可调电源手动调节电压&am…

suse15 系统分区信息损坏修复案例一则

关键词 suse linux、系统分区fdisk、分区类型testdisk、grub2、bios There are many things that can not be broken&#xff01; 如果觉得本文对你有帮助&#xff0c;欢迎点赞、收藏、评论&#xff01; 一、问题现象 业务反馈一台suse服务器&#xff0c;因错误执行了fdisk分区…

Linux 服务器硬件及RAID配置实战

服务器详解 服务器分类 可以分为&#xff1a;塔式服务器、机架服务器、刀片服务器、机柜服务器等。 其中以机架式居多 服务器架构 服务器品牌&#xff1a; 戴尔、AMD、英特尔、惠普、华为、华3&#xff08;H3C&#xff09;、联想、浪潮、长城 服务器规格&#xff1a; 规格…

【 C++ 】 讲解与实现 对数器接口

什么是对数器 说是叫对数器&#xff0c;但我觉得叫做核验器更好。 为什么&#xff1f;因为其作用是核验算法是否正确&#xff0c;所以我觉得叫核验器更好。 注意&#xff1a;本文实现的是生成只能int类型的对数器&#xff0c;其余类型不支持。 对数器的原理 对于一个核验器&am…

怎么用手机远程控制电脑 远程控制怎么用

怎么用手机远程控制电脑&#xff1a;远程控制怎么用 在这个科技日新月异的时代&#xff0c;远程控制电脑已经成为了很多人的需求。有时&#xff0c;我们可能在外出时突然需要访问家中的电脑&#xff0c;或者在工作中需要远程操控办公室的电脑。这时&#xff0c;如果能用手机远…

layui框架实战案例(27):弹出二次验证

HTML容器 <button class"layui-btn layui-btn-sm layui-btn-danger" lay-event"delete"><i class"layui-icon layui-icon-delete"></i>批量删除</button>删除封装函数 function delAll(school_id, school_name) {var lo…

Springboot+Vue项目-基于Java+MySQL的网上超市系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

【信号处理】基于EEG脑电信号的自闭症预测典型方法实现

理论 自闭者主要受到遗传和环境因素的共同影响。由于自闭症是一种谱系障碍&#xff0c;因此每个自闭症患者都有独特的优势和挑战。自闭症患者学习、思考和解决问题的方式可以是高技能的&#xff0c;也可以是严峻的挑战。研究表明&#xff0c;高质量的早期干预可以改善学习、沟…

ZStack教育云计算解决方案入选高质量数字化转型技术解决方案集

近日&#xff0c;中国信通院“铸基计划”《高质量数字化转型技术解决方案&#xff08;2023年度&#xff09;》&#xff08;以下简称“方案集”&#xff09;发布&#xff0c;云轴科技ZStack智慧教育云计算解决方案入选《高质量数字化转型技术解决方案集》。 为促进数字化转型相…

第63天:服务攻防-框架安全CVE 复现DjangoFlaskNode.JSJQuery

目录 思维导图 案例一&#xff1a;JavaScript-开发框架安全-Jquery&Node node.js目录穿越 CVE-2021-21315命令执行 Jquery CVE-2018-9207 案例二&#xff1a;Python-开发框架安全-Django&Flask django cve_2019_14234 CVE-2021-35042 flask ssti 思维导图 案…

Jenkins和gitlab实现CICD

1 背景 在开发TracerBackend服务的时候&#xff0c;每次更改代码之后需要推送到gitlab&#xff0c;然后ssh登录到Ubuntu的服务器上部署新的代码。服务成功启动之后&#xff0c;在本地执行测试用例&#xff0c;觉得这一套操作流程还是挺复杂的。想起公司的代码发布流程&#xf…

多模态视觉语言模型:BLIP和BLIP2

1. BLIP BLIP: Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generation BLIP的总体结构如下所示&#xff0c;主要包括三部分&#xff1a; 单模态编码器&#xff08;Image encoder/Text encoder&#xff09;&#xff1a;分别进…

智慧浪潮下的产业园区:解读智慧化转型如何打造高效、绿色、安全的新产业高地

随着信息技术的飞速发展&#xff0c;智慧化转型已经成为产业园区发展的重要趋势。在智慧浪潮的推动下&#xff0c;产业园区通过集成应用物联网、大数据、云计算、人工智能等先进技术手段&#xff0c;实现园区的智慧化、高效化、绿色化和安全化&#xff0c;从而打造成为新产业高…

(四)SQL面试题(连续登录、近N日留存)学习简要笔记 #CDA学习打卡

目录 一. 连续登录N天的用户数量 1&#xff09;举例题目 2&#xff09;分析思路 3&#xff09;解题步骤 &#xff08;a&#xff09;Step1&#xff1a;选择12月的记录&#xff0c;并根据用户ID和登录日期先去重 &#xff08;b&#xff09;Step2&#xff1a;创建辅助列a_rk…

数字接龙(蓝桥杯)

文章目录 数字接龙【问题描述】解题思路DFS 数字接龙 【问题描述】 小蓝最近迷上了一款名为《数字接龙》的迷宫游戏&#xff0c;游戏在一个大小为N N 的格子棋盘上展开&#xff0c;其中每一个格子处都有着一个 0 . . . K − 1 之间的整数。游戏规则如下&#xff1a; 从左上…

使用Python进行云计算:AWS、Azure、和Google Cloud的比较

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 使用Python进行云计算&#xff1a;AWS、Azure、和Google Cloud的比较 随着云计算的普及&am…