各大排序算法

2024-05-26 20:44
文章标签 算法 排序 各大

本文主要是介绍各大排序算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

插入排序

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

冒泡排序

快速排序

选择排序

归并排序


插入排序

插入排序的基本思想是,将N个待排序元素分为一组有序表和一个无序表,一开始有序表只有一个元素,无序表中有N-1个元素,排序过程中每次取无序表的第一个元素依次与有序表的元素进行对比,插入到适当的位置使之成为新的有序表

我们可以看到在插入排序中排序需要进行的轮次是N-1轮(第一个元素默认有序),那么接下来看这个例子的第一轮的代码实现,以及代码解释

这就是得到的第一轮的结果,那么第二、第三轮也是一样如此操作,我们只需使用外部循环条件即可实现

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

在进行希尔排序的介绍之前我们先来看看插入排序的一些问题,现在我们给出这样一组数据[2,3,4,5,6,1], 那么他在进行插入排序时会产生很多次数的位移

对于这种极端情况,那么插入排序就显得效率低下,而希尔排序也是插入排序的一种,属于优化后的插入排序

希尔排序的思想是把记录按下标的一定增量进行分组,对每组进行插入排序进行排序,随着增量的逐渐减小,每组包含的元素越来越多,当增量缩小为1时,整个数组被分为一组,进行插入排序后则有序

这种是交换法的希尔排序,图中的进行插入排序其实不是真正的直接插入排序,而只是对比两个数进行交换

这就是希尔排序的图解,接下来我们来看代码实现

根据希尔三次排序我们可以摸索到希尔排序的循环规则,接下来我们将其整合起来

import java.util.Arrays;public class MyShellSort {public static void main(String[] args) {int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};ShellSort(arr);}//使用逐步推导的方式进行分析希尔排序public static void ShellSort(int[] arr) {int temp = 0; //定义临时变量,用于交换int count = 0;for(int gap = arr.length/2;gap>0;gap/=2){for (int i = gap; i < arr.length; i++) {for (int j = i - gap; j >= 0; j -= gap) {//如果当前元素大于加上增量后的那个元素,进行交换if (arr[j] > arr[j + gap]) {temp = arr[j];arr[j] = arr[j + gap];arr[j + gap] = temp;}}}System.out.println("第"+(++count)+"轮希尔排序后的结果:" + Arrays.toString(arr));}}
}

我们看这段代码可以发现他嵌套了三个for循环,可想而知他的效率肯定是不高的,而介绍这种交换法的希尔排序主要是让我们理解增量这个概念,接下来我们来学习希尔排序移动法(进行直接插入排序)

移动法的希尔排序就是在分组后,对各组进行排序不是使用简单的交换,而是进行直接插入排序,即找到了合适的位置才插入(交换法效率低下的原因就是因为无脑进行交换,导致交换量过大)

import java.util.Arrays;public class MyShellSort {public static void main(String[] args) {int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};System.out.println("希尔排序前的数组"+Arrays.toString(arr));ShellSort(arr);System.out.println("希尔排序后的数组"+Arrays.toString(arr));}//使用逐步推导的方式进行分析希尔排序//交换法希尔排序public static void ShellSort(int[] arr) {for(int gap = arr.length/2;gap>0;gap/=2){//从gap元素开始,在对其所在的组进行插入排序for (int i = gap;i<arr.length;i++){int j = i;int temp = arr[j];//待插入元素if(arr[j] < arr[j-gap]){while(j-gap >= 0 && temp < arr[j-gap]){arr[j] = arr[j-gap]; //arr[j]这个待插入数的前一个数向后挪j -= gap;}//当退出while循环后,说明找到了合适的位置了arr[j] = temp;}}}}
}

这就是移动法的希尔排序中间的排序逻辑和插入排序类似,只是多引出了gap增量这个概念

冒泡排序

冒泡排序的基本思想是,对待待排序序列从前向后,依次比较相邻元素的值,如果发现逆序则进行交换,使值较大的元素逐渐从前移向后部(从小到大排序),就像水底的气泡向上冒

接下来给出一幅图理解冒泡排序的过程

在这张冒泡排序图解中我们可以发现,一共进行数组长度-1次循环,每一趟都使得当趟最大的值回到适合的位置,并且每趟排序的次数都在逐渐减少(因为已经有元素到了适合的位置)

接下来就来看看冒泡排序的代码演示,为了方便理解我将一步一步拆分

看这样的一段冒泡排序分解代码,想必你已经发现规律了,for循环体都是一样的,交换思想也是一样(使用临时变量存储值),那么我们就可以使用一个for循环将其整理为以下代码

import java.util.Arrays;public class MyBubbleSort {public static void main(String[] args) {int[] arr = {3,9,-1,10,-2};BubbleSort(arr);}public static void BubbleSort(int[] arr){//第一趟排序将最大的值放在倒数第一位for(int i = 0;i< arr.length-1;i++){for(int j = 0;j<arr.length-1-i;j++){int temp = 0;//临时变量,进行交换元素时用到if(arr[j]>arr[j+1]){//如果前面的元素比后面元素大,则进行交换(从小到大排序)temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}System.out.println("第"+(i+1)+"趟排序后的结果:");System.out.println(Arrays.toString(arr));}

我们可以知道这种for循环嵌套for循环的时间复杂度是O(n^2),这样写的代码还不是最完美的,还可以进行优化,首先先看下面这一组数据的冒泡排序过程

从这组数据中我们可以发现,在第二趟交换了一次之后,数据已经变得有序了,那么接下来的操作都只是走"过程"而已

那么我们优化的思路就是,如果在某一趟排序中,没有发生任何一次的交换,那么就可以提前结束冒泡排序

这样我们就减少了一趟循环,也就优化成功了

快速排序

快速排序是对冒泡排序算法的一种改进,快速排序的基本思想是将要排序的数据分割成独立的两个部分,其中一部分的所有数据要比另外一部分的所有数据都要小,然后再按从方法对独立的两个部分进行快速排序,直到变得有序

接下来的的各种细节看代码演示,以及注解 

import java.util.Arrays;public class MyQuickSort {public static void main(String[] args) {int[] arr = {-9,78,0,23,-567,70};//定义每段快速排序的最左边的位置int left = 0;//定义每段快速排序最右边的位置int right = arr.length-1;QuickSort(arr,left,right);System.out.println(Arrays.toString(arr));}public static void QuickSort(int[] arr,int l,int r){if(l<r){int pivotpos = partition(arr,l,r);//将pivot左边部分进行快速排序QuickSort(arr,l,pivotpos-1);//将pivot右边部分进行快速排序QuickSort(arr,pivotpos+1,r);}}public static int partition(int[] arr,int l,int r){int pivot = arr[l];while (l<r){//每次都要进行是否l<r的判断(因为极端的情况下比如右边都没有比pivot小的数,那么就会一直向前到不满足l<r)while(l<r&&arr[r]>=pivot){//若当前这个数不是比pivot小那么就进行向前找r--;}//跳出循环说明右边找到小于pivot的数了//那么就让其填入left的位置arr[l] = arr[r];while (l<r&&arr[l]<=pivot){//若当前这个数不是比pivot大那么就进行向后找l++;}//跳出循环说明找到了比pivot大的数//那么就让将其填入right的位置arr[r] = arr[l];}//这里也可以写arr[r] = pivot 因为跳出了大循环说明他们相遇了arr[l] = pivot;//放回当前相遇的位置,以便后续进行两部分的快速排序return l;}
}

选择排序

选择排序的基本思想很简单,就是在一组待排序数组arr中,第一次从arr[0]~arr[n-1]中寻找最小的元素,与arr[0]进行交换,第二次从arr[1]~arr[n-1]中寻找第二小的数与arr[1]进行交换,一直寻找当前待排序列的最小数进行交换直到有序为止

看着这张图进行选择排序的说明:

  • 选择排序一共有数组大小-1轮排序
  • 每一轮排序又是一个循环(目的是找到当前轮次最小值)

每一轮循环的规则如下:

  • 先假定当前元素是最小元素,然后与每个元素进行比较,遇到比当前最小元素小的数,则重新设置其为最小元素,并且得到他的下标,当遍历完数组后就得到了最小数以及其下标,最后进行交换即可

接下来看选择排序的代码实现

通过上方的分解代码我们也找到了规律,那么现在用外层嵌套for循环来完成

归并排序

归并排序的基本思想是采用分治策略(将大问题分成小问题然后递归求解),归并排序的思想比较难理解,我们先看案例图解

分阶段可以理解为递归拆分子序列,而治阶段可以简单用一幅图介绍

接下来通过代码以及注释来理解归并排序

import java.util.Arrays;public class MyMergeSort {public static void main(String[] args) {int[] arr = {8,4,5,7,1,3,6,2};int[] temp = new int[arr.length]; //归并排序需要额外的空间mergeSort(arr,0, arr.length -1,temp);System.out.println(Arrays.toString(arr));}//分+合的代码public static void mergeSort(int[] arr,int left,int right,int[] temp){if(left<right){int mid = (left+right)/2;//先向左递归进行分解mergeSort(arr,left,mid,temp);//向右递归进行分解mergeSort(arr,mid+1,right,temp);//进行合并merge(arr,left,mid,right,temp);}}/**** @param arr //排序的原始数组* @param left //左边有序序列的初识索引* @param mid //中间索引* @param right //右边索引* @param temp //临时数组*///合并的代码public static void merge(int[] arr,int left,int mid,int right,int[] temp){int i = left; //初始化i,左边有序序列的初识索引int j = mid+1; //初始化j,右边有序序列的初识索引int t = 0; //做为临时数组的索引//1.先把左右两边的有序序列按照规则,填充到临时数组,直到有一方完全填充完为止while (i<=mid && j<=right){if(arr[i]<=arr[j]){//左边的值比右边小,则填充左边的到临时数组,并且i++temp[t] = arr[i];i+=1;t+=1;  //临时数组的索引后移}else {//右边的值比左边小,则填充右边的到临时数组,并且j++temp[t] = arr[j];j+=1;t+=1;}}//2.把有剩余数据的一边依次全部填充到临时数组while(i<=mid){//说明左边没有填充完毕temp[t] = arr[i];i+=1;t+=1;}while (j<=right){//说明右边没有填充完毕temp[t] = arr[j];j+=1;t+=1;}//3.将临时数组元素拷贝到原数组//注意:这里不是每次都拷贝所有数据,比如进行递归的第一次合并,拷贝到原数组的元素只有0 1t = 0;int tempLeft = left; //并不是每次拷贝都是从头开始的,left拷贝的值是会发送变化的while (tempLeft<=right){ //第一次合并tempLeft=0 right=1 / 第二次合并tempLeft=2 right=3......arr[tempLeft] = temp[t];tempLeft+=1;t+=1;}}
}

这里需要注意的是理解递归的过程,合并并不是我们上方举例的最后一次合并,第一次合并只是合并两个元素,我们加上一些代码进行对合并内部的理解,结果为

这篇关于各大排序算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1005542

相关文章

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

Mybatis 传参与排序模糊查询功能实现

《Mybatis传参与排序模糊查询功能实现》:本文主要介绍Mybatis传参与排序模糊查询功能实现,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、#{ }和${ }传参的区别二、排序三、like查询四、数据库连接池五、mysql 开发企业规范一、#{ }和${ }传参的

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

C++快速排序超详细讲解

《C++快速排序超详细讲解》快速排序是一种高效的排序算法,通过分治法将数组划分为两部分,递归排序,直到整个数组有序,通过代码解析和示例,详细解释了快速排序的工作原理和实现过程,需要的朋友可以参考下... 目录一、快速排序原理二、快速排序标准代码三、代码解析四、使用while循环的快速排序1.代码代码1.由快

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

Spring排序机制之接口与注解的使用方法

《Spring排序机制之接口与注解的使用方法》本文介绍了Spring中多种排序机制,包括Ordered接口、PriorityOrdered接口、@Order注解和@Priority注解,提供了详细示例... 目录一、Spring 排序的需求场景二、Spring 中的排序机制1、Ordered 接口2、Pri