目录

一、【前言】排序的稳定性:

二、七大排序总览

三、插入排序

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 ++;}}}

七大排序这一篇归总了六种,还剩交换排序的一种:快排,快排易考又内容较多,下一篇再详细总结!

七大排序算法大汇总(上)相关推荐

  1. 计算机一级电子表格地区排名,玩转Excel电子表格排序方法大汇总

    玩转Excel电子表格排序方法大汇总 分类:计算机等级 | 更新时间:2016-07-08| 来源:转载 排序是数据处理中的经常性工作,Excel排序有序数计算(类似成绩统计中的名次)和数据重排两类. ...

  2. ANDROID内存优化(大汇总——上)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上 ...

  3. 大模型 NLP 算法 大汇总

    大模型 & NLP & 算法 大汇总 订阅本专栏[大模型 & NLP & 算法 知识大礼包],即可获取博主多年积累的关于 [大模型 & NLP & 算法 ...

  4. 七大排序算法的个人总结(三)

    堆排序(Heap): 要讲堆排序之前先要来复习一下完全二叉树的知识. 定义: 对一棵具有n个结点的二叉树按层序编号,如果编号为i(0 <= i <= n)的结点与同样深度的满二叉树编号为i ...

  5. 七大排序算法的个人总结(二)

    归并排序(Merge Sort): 归并排序是一个相当"稳定"的算法对于其它排序算法,比如希尔排序,快速排序和堆排序而言,这些算法有所谓的最好与最坏情况.而归并排序的时间复杂度是固 ...

  6. 七大排序算法的个人总结(一)

    冒泡排序(Bubble Sort): 很多人听到排序第一个想到的应该就是冒泡排序了.也确实,冒泡排序的想法非常的简单:大的东西沉底,汽泡上升.基于这种思想,我们可以获得第一个版本的冒泡: public ...

  7. 排序算法-- 大总结

    一.冒泡排序: 1.概念 它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成. 2.复杂度分析 ...

  8. 【算法入门漫画】:“排序算法” 大总结

    冒泡排序: 漫画:什么是冒泡排序? 选择排序: 漫画:什么是选择排序? 插入排序: 漫画:什么是插入排序? 此外还有冒泡排序的变种,鸡尾酒排序: 漫画:什么是鸡尾酒排序? 第三梯队的排序算法有什么共同 ...

  9. c++ 二维数组 排序_漫画:“排序算法” 大总结

    ​冒泡排序: 漫画:什么是冒泡排序?​mp.weixin.qq.com 选择排序: 漫画:什么是选择排序?​mp.weixin.qq.com 插入排序: 漫画:什么是插入排序?​mp.weixin.q ...

最新文章

  1. linux下把进程绑定到特定cpu核上运行
  2. leetcode414. 第三大的数
  3. 博士当中学老师是“人才浪费”?
  4. 猜数字四个数字不重复C语言,c语言题 猜数字游戏
  5. python采集代理ip_Python采集代理ip并判断是否可用和定时更新的方法
  6. 产品读书《一页纸项目管理》
  7. 生产环境 JDK6 升级 JDK8
  8. 浅谈图像识别技术原理与价值
  9. YDOOK: ANSYS Maxwell 19 教程20:Maxwell 2D Surface Approximation 网格划分
  10. 弗洛伊德本我、自我、超我理论
  11. 太阳能心率智能骑行仪(STM32)
  12. iphone连电脑服务器未响应,今天要闻iphone8无限转圈黑屏强制关机没反应(苹果电脑开不了机黑屏)...
  13. 服务器与微信联通,联通微信和钉钉服务
  14. samba服务器搭建详细配置
  15. 抖音电商主播考核绩效直播运营KPI方案
  16. Excel学习——countif函数
  17. 【笔记】Spring - Spring Data 整合:SpringBoot、Redis
  18. ubuntu远程桌面软件
  19. Unix高级安全设置
  20. 机器学习知识总结 —— 14. 什么是支持向量机(基础概念、梯度下降、软间隔、硬间隔)?

热门文章

  1. 大学计算机西安电子科技大学答案,计算机网络技术与应用课后题答案(西安电子科技大学)...
  2. 杰理之MIC 免电容方案需要设置【篇】
  3. 微服务学习笔记 K8S、ISTIO、微服务、容器不得不说的故事
  4. p12解析流程_解析p12格式的程序(提取公钥和私钥)
  5. >>数据管理:DAMA简介
  6. 12月3日DAMA-CDGA/CDGP数据治理认证考试报名
  7. 6.1.4 6.1.5完美越狱
  8. freemarker的第一个例子
  9. [易飞]ERP附件与电子签核集成
  10. 20英里还是60英里?