七大排序算法大汇总(上)
目录
一、【前言】排序的稳定性:
二、七大排序总览
三、插入排序
1.1直接插入排序
1.2直接插入排序优化版——折半插入排序:
2.希尔排序
四、选择排序
1.1选择排序
1.2进阶版选择排序
2.堆排序
五、交换排序
1.冒泡排序
六、归并排序
1.1归并排序(递归)
1.2归并排序非递归写法
一、【前言】排序的稳定性:
稳定性:两个相等的数据,如果经过排序后,排序算法能保证其相对位置不发生变化,则我们称该算法是具备稳定性的排序算法。
如下面这个例子,未排序前,5(a)是在5(b)前面的,因为5=5,所以如果排序后5(a)仍然是在5(b)前面,保证了相等的数排完序之后的相对位置仍然未变,我们就说这种排序是稳定的
那么,为什么有时候需要保证稳定性呢,来看一个典型的现实例子:
某商城订单默认是按时间顺序排列,现需要按照订单金额大小从小到大排序,而对于金额大小一样的订单,排序后保证其时间顺序不变,这就一定是需要具有稳定性的算法来排序了
二、七大排序总览
三、插入排序
1.1直接插入排序
算法思想:
将数据分为已排序区间【0,i)和待排序区间【i,length - 1】,遍历待排序区间,将一个一个元素插入到已排序区间的合适位置,直至待排序区间为空
代码实现:
/*** 插入排序** @param arr*/public static void insert(int[] arr) {int n = arr.length;
// i是待排序去插入已排序区间的元素for (int i = 1; i < n; i++) {
// 从i开始向前不断交换,直至i大于等于前一个元素for (int j = i; j > 0; j--) {
// 这里的等号一定要取到,相等了也不交换,保持稳定性if (arr[j] >= arr[j - 1]) {break;} else {swap(arr, j, j - 1);}}}}private static void swap(int[] arr, int a, int b) {int temp = arr[a];arr[a] = arr[b];arr[b] = temp;}
1.2直接插入排序优化版——折半插入排序:
所谓优化版,就是在找插入位置时,不似刚才的逆序遍历找位置,而是用二分法,查找待插入元素的插入位置
/*** 折半插入排序* @param arr*/public static void midInsert(int[] arr){int n = arr.length;for (int i = 1; i < n; i++) {int left = 0;int right = i;int cur = arr[i];while(left < right){int mid = left + (right - left) / 2;if(arr[mid] > cur){right = mid;}else{left = mid + 1 ;}}
// 此时,left位置就是待插入位置,需要将【left,i)之间的元素后移一位for (int j = i; j > left; j--) {arr[j] = arr[j - 1];}arr[left] = cur;}}
2.希尔排序
前面的插入排序我们可以看出,当一组数据近乎有序时,插入排序的时间复杂度近乎为O(N),所以当数组近乎有序时,插入排序效率还是很高的,希尔排序正是借助了这一点。
【注】因为希尔排序在分组交换中会改变数组中元素相对位置,故而希尔排序无稳定性
整体思路:
数组长度为N,先将数组分为N/2组,保证各小组里的数据是有序的(插入排序),每排序一次就将组数/2,并保证组内元素有序,直至最后组数为1;
当组数为1时,此时数组是近乎有序的,故而最后再使用一次插入排序,至此,数组排序结束
代码实现:
/*** 希尔排序* @param arr*/public static void shellSort(int[] arr){int n = arr.length;int gap = n >> 1;
// 先分组排序while(gap >= 1){shell(arr,gap);gap = gap >> 1;}
// 最后插入排序
// insert(arr);}// 数组中相隔gap个单位的元素为一组,保证组内元素有序private static void shell(int[] arr, int gap) {for (int i = gap; i < arr.length; i++) {int val = arr[gap];for (int j = i; j - gap >= 0; j -= gap) {if(arr[j] >= arr[j - gap]){break;}else{swap(arr,j,j - gap);}}}}
四、选择排序
1.1选择排序
选择排序,顾名思义,就是一个个把合适位置的元素挑起来放在其合适的位置。那么合适的元素怎么选呢
两种方法,一种是找最大,一种是找最小;
以找最大元素为例,第一次遍历数组,找到最大元素,该元素应当放在最后一个索引位置处,然后找次大的元素,放在倒数第二处位置,……直至全部选择放置结束
代码实现:
/*** 选择排序* @param arr*/public static void selectMax(int[] arr){int n = arr.length;
// 只需n-1趟for (int i = n - 1; i > 0; i--) {int max = Integer.MIN_VALUE;int index = 0;
// 遍历一遍,找最大for (int j = 0; j <= i; j++) {if(arr[j] >= max){index = j;max = arr[j];}}
// 交换最大值和正确的位置元素swap(arr,index,i);}}
1.2进阶版选择排序
每次遍历找最大或最小,需要遍历n - 1次,那么我们思考,可不可以一次遍历同时找到最大和最小,并将这两个元素各自放在最前和最后,这样,效率至少提高了一倍
【唯一需要注意的是,如若先交换较小元素,在交换较大元素时,需注意看是否大元素恰好在上一步交换时已经被改变了索引】
代码实现:
/*** 进阶版选择排序* @param arr*/public static void selectMaxMin(int[] arr){int n = arr.length;int left = 0;int right = arr.length - 1;
// 当left和right中间只有一个元素是,数组已经有序while(left < right){int min = left;int max = left;
// 遍历中间区间,找最大和最小for (int i = left + 1; i <= right; i++) {if(arr[min] > arr[i]){min = i;}if(arr[max] < arr[i]){max = i;}}
// 先把最小的交换swap(arr,min,left);
// 然后交换最大,此时需注意有可能最大恰好在上一步被交换了if(max == left){max = min;}swap(arr,max,right);
// 最后记得让左右区间各扩大一个left ++;right --;}}private void swap(int[] arr, int a, int b) {int temp = arr[a];arr[a] = arr[b];arr[b] = temp;}
2.堆排序
堆排序也是选择排序的一种,它也是在找最大或最小元素实现排序,在堆的笔记中我们已经详细介绍过,这里不再赘述。堆排序也不具有稳定性
代码附上:
public static void heapSort(int[] arr) {
// 先将数组arr堆化,从第一个非叶子节点开始下沉操作for (int i = (arr.length - 1 - 1) / 2; i >= 0; i--) {siftDown(arr, i, arr.length);}
// 此时已是最大堆for (int i = arr.length - 1; i > 0; i--) {
// 将最大的元素移至最后一个位置,次大的元素移至倒数第二个位置……swap(arr, 0, i);
// 每次换好一个位置后,将其余元素下沉重新堆化siftDown(arr, 0, i);}}// 交换private static void swap(int[] arr, int index1, int index2) {int temp = arr[index1];arr[index1] = arr[index2];arr[index2] = temp;}/*** 下沉操作(大根堆)** @param arr* @param i 开始下沉的位置* @param length 下沉的长度*/public static void siftDown(int[] arr, int i, int length) {
// 当还有子节点时while ((2 * i + 1) < length) {int j = 2 * i + 1;if (j + 1 < length && arr[j + 1] > arr[j]) {j = j + 1;}if (arr[i] < arr[j]) {swap(arr, i, j);i = j;} else {break;}}}
五、交换排序
1.冒泡排序
冒泡排序思路这里简单说下,冒泡就是一趟趟将最大的元素交换至最后的位置,将次大的元素交换至倒数第二位,直至走完n趟
仅注意一点:
冒泡的优化:当进行到某趟时,发现无可交换的元素,说明这时整个数据已经有序,无需继续后面的趟数,可以提前break
代码实现:
/*** 冒泡排序* @param arr*/public static void bubbleSort(int[] arr){int n = arr.length;
// i控制趟数for (int i = 0; i < n; i++) {boolean a = true;for (int j = 0; j < n - i - 1; j++) {
// 不取等保证稳定性if(arr[j + 1] < arr[j]){swap(arr,j + 1,j);a = false;}}
// 如果走至某趟已经无交换,可提前结束循环if(a == true){break;}}}
六、归并排序
1.1归并排序(递归)
归并:先分再合
整体思路:
先将数组逐次拆分,直至其拆分为一个个元素;然后依据拆分步骤倒着往回合并
【注】这种方法是具有稳定性的
两个地方可以优化:
- 没必要一直拆分到一个元素,当区间已经较小时,可以选择使用插入排序来完成排序,一般,当拆分区间小于15个元素时,我们就可以采用插入排序
- 在进行区间两两合并时,可以先判断一下两个区间里的元素是否需要重新排序;我们知道,当左区间最后一个元素小于右区间第一个元素时,说明整个左区间都比右区间小,说明这两个区间合并时无需重新排序
代码实现:
/*** 归并排序(递归做法)* @param arr*/public static void mergeSort(int[] arr){merge(arr,0,arr.length - 1);}
// 将l到r区间内的元素进行归并排序public static void merge(int[]arr,int l,int r){
// 一般如果区间元素已经小于15个,就可以不再归并,直接插入排序即可if(r - l <= 15){insertHelpMerge(arr,l,r);return;}int mid = l + ((r - l) >> 1);
// 递归左merge(arr,l,mid);
// 递归右merge(arr,mid + 1,r);
// 当左右合起来仍无序时,才需要左右合并if(arr[mid] > arr[mid + 1]){mergeHelp(arr,l,mid,r);}}// 合并以mid为界的两个子数组private static void mergeHelp(int[] arr, int l, int mid, int r) {// 辅助数组暂存l到r间的元素int[] res = new int[r - l + 1];for (int i = 0; i < res.length; i++) {res[i] = arr[l + i];}int left = l;int right = mid + 1;
// 按照排序结果修改arr区间元素for (int index = l; index <= r; index++) {
// 如果左边已全部入序,剩余的直接为右边的if(left > mid){arr[index] = res[right - l];right ++;}else if(right > r){arr[index] = res[left - l];left ++;}else if(res[left - l] <= res[right - l]){arr[index] = res[left - l];left ++;}else{arr[index] = res[right - l];right ++;}}}//l到r区间内的元素进行插入排序private static void insertHelpMerge(int[] arr, int l, int r) {for (int i = l + 1; i <= r; i++) {for (int j = i; j > l; j--) {if(arr[j] >= arr[j - 1]){break;}else{swap(arr,j,j - 1);}}}}
1.2归并排序非递归写法
/*** 归并做法的非递归写法* @param arr*/public static void mergeSortNoRecursion(int[] arr){
// 第一层for循环指示合并元素的个数for (int i = 1; i <= arr.length; i += i) {
// j + i 就是右区间开始的索引for (int j = 0; j + i < arr.length; j += i + i) {mergeHelp(arr,j,j + i - 1,Math.min(arr.length - 1,j + i + i - 1));}}}// 合并以mid为界的两个子数组private static void mergeHelp(int[] arr, int l, int mid, int r) {// 辅助数组暂存l到r间的元素int[] res = new int[r - l + 1];for (int i = 0; i < res.length; i++) {res[i] = arr[l + i];}int left = l;int right = mid + 1;
// 按照排序结果修改arr区间元素for (int index = l; index <= r; index++) {
// 如果左边已全部入序,剩余的直接为右边的if(left > mid){arr[index] = res[right - l];right ++;}else if(right > r){arr[index] = res[left - l];left ++;}else if(res[left - l] <= res[right - l]){arr[index] = res[left - l];left ++;}else{arr[index] = res[right - l];right ++;}}}
七大排序这一篇归总了六种,还剩交换排序的一种:快排,快排易考又内容较多,下一篇再详细总结!
七大排序算法大汇总(上)相关推荐
- 计算机一级电子表格地区排名,玩转Excel电子表格排序方法大汇总
玩转Excel电子表格排序方法大汇总 分类:计算机等级 | 更新时间:2016-07-08| 来源:转载 排序是数据处理中的经常性工作,Excel排序有序数计算(类似成绩统计中的名次)和数据重排两类. ...
- ANDROID内存优化(大汇总——上)
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上 ...
- 大模型 NLP 算法 大汇总
大模型 & NLP & 算法 大汇总 订阅本专栏[大模型 & NLP & 算法 知识大礼包],即可获取博主多年积累的关于 [大模型 & NLP & 算法 ...
- 七大排序算法的个人总结(三)
堆排序(Heap): 要讲堆排序之前先要来复习一下完全二叉树的知识. 定义: 对一棵具有n个结点的二叉树按层序编号,如果编号为i(0 <= i <= n)的结点与同样深度的满二叉树编号为i ...
- 七大排序算法的个人总结(二)
归并排序(Merge Sort): 归并排序是一个相当"稳定"的算法对于其它排序算法,比如希尔排序,快速排序和堆排序而言,这些算法有所谓的最好与最坏情况.而归并排序的时间复杂度是固 ...
- 七大排序算法的个人总结(一)
冒泡排序(Bubble Sort): 很多人听到排序第一个想到的应该就是冒泡排序了.也确实,冒泡排序的想法非常的简单:大的东西沉底,汽泡上升.基于这种思想,我们可以获得第一个版本的冒泡: public ...
- 排序算法-- 大总结
一.冒泡排序: 1.概念 它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成. 2.复杂度分析 ...
- 【算法入门漫画】:“排序算法” 大总结
冒泡排序: 漫画:什么是冒泡排序? 选择排序: 漫画:什么是选择排序? 插入排序: 漫画:什么是插入排序? 此外还有冒泡排序的变种,鸡尾酒排序: 漫画:什么是鸡尾酒排序? 第三梯队的排序算法有什么共同 ...
- c++ 二维数组 排序_漫画:“排序算法” 大总结
冒泡排序: 漫画:什么是冒泡排序?mp.weixin.qq.com 选择排序: 漫画:什么是选择排序?mp.weixin.qq.com 插入排序: 漫画:什么是插入排序?mp.weixin.q ...
最新文章
- linux下把进程绑定到特定cpu核上运行
- leetcode414. 第三大的数
- 博士当中学老师是“人才浪费”?
- 猜数字四个数字不重复C语言,c语言题 猜数字游戏
- python采集代理ip_Python采集代理ip并判断是否可用和定时更新的方法
- 产品读书《一页纸项目管理》
- 生产环境 JDK6 升级 JDK8
- 浅谈图像识别技术原理与价值
- YDOOK: ANSYS Maxwell 19 教程20:Maxwell 2D Surface Approximation 网格划分
- 弗洛伊德本我、自我、超我理论
- 太阳能心率智能骑行仪(STM32)
- iphone连电脑服务器未响应,今天要闻iphone8无限转圈黑屏强制关机没反应(苹果电脑开不了机黑屏)...
- 服务器与微信联通,联通微信和钉钉服务
- samba服务器搭建详细配置
- 抖音电商主播考核绩效直播运营KPI方案
- Excel学习——countif函数
- 【笔记】Spring - Spring Data 整合:SpringBoot、Redis
- ubuntu远程桌面软件
- Unix高级安全设置
- 机器学习知识总结 —— 14. 什么是支持向量机(基础概念、梯度下降、软间隔、硬间隔)?
热门文章
- 大学计算机西安电子科技大学答案,计算机网络技术与应用课后题答案(西安电子科技大学)...
- 杰理之MIC 免电容方案需要设置【篇】
- 微服务学习笔记 K8S、ISTIO、微服务、容器不得不说的故事
- p12解析流程_解析p12格式的程序(提取公钥和私钥)
- >>数据管理:DAMA简介
- 12月3日DAMA-CDGA/CDGP数据治理认证考试报名
- 6.1.4 6.1.5完美越狱
- freemarker的第一个例子
- [易飞]ERP附件与电子签核集成
- 20英里还是60英里?