(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第1张

目录

冒泡排序(BubbleSort):

代码详解:

 冒泡排序的优化:

 选择排序(SelectSort):

代码详解:

 插入排序(InsertSort):

代码详解:

 希尔排序(ShellSort):

 法一(交换法)代码详解:

 法二(移位法-->插入排序的优化)代码详解:

快速排序(QuickSort): 

代码详解:

 归并排序(MergetSort):

代码详解:

 基数排序(RadixSort):

代码详解:

堆排序:

最后,一张图概括: 


冒泡排序(BubbleSort):

冒泡排序(BubbleSort)的基本思路:通过对待排序从前往后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前往后移动,较小的往上挪动,就像水底下的气泡一样逐渐向上冒。

图解:

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第2张

代码详解:

public class BubbleSort { public static void main(String[] args){ Scanner sc=new Scanner(System.in); int[] arr=new int[5];//假设测试案例只有五个数字 System.out.print("请输入要排序的数组:"); for(int i=0;i<5;i++){ arr[i]=sc.nextInt(); } bubblesort(arr); System.out.println(); System.out.print("请输出排序好的数组:"+Arrays.toString(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++){ if(arr[j]>arr[j+1]){ int temp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=temp; } } System.out.printf("第%d趟排序后的数组",i+1);//展示每次冒泡排序的过程 System.out.println(Arrays.toString(arr));//将数组转化为字符串的形式输出 } } }

运行结果:(通过运行结果来展示“气泡”向上挪动的过程,较大的数逐渐沉底)

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第3张

 冒泡排序的优化:

因为排序的过程中,各个元素不断接近自己要排好时所对应的位置,如果一趟比较下来没有进行交换,就说明序列有序。通过设置一个标志flag判断元素是否进行过交换,从而减少不必要的比较。

优化代码:

// 将前面额冒泡排序算法,封装成一个方法 public static void bubbleSort(int[] arr) { // 冒泡排序 的时间复杂度 O(n^2), 自己写出 int temp = 0; // 临时变量 boolean flag = false; // 标识变量,表示是否进行过交换 for (int i = 0; i < arr.length - 1; i++) { for (int j = 0; j < arr.length - 1 - i; j++) { // 如果前面的数比后面的数大,则交换 if (arr[j] > arr[j + 1]) { flag = true; temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } //System.out.println("第" + (i + 1) + "趟排序后的数组"); //System.out.println(Arrays.toString(arr)); if (!flag) { // 在一趟排序中,一次交换都没有发生过 break; } else { flag = false; // 重置flag!!!, 进行下次判断 } } }

时间复杂度:最坏情况:O(N^2)
      最好情况:O(N)

空间复杂度:O(1)

小结冒泡排序规则

(1) 一共进行 数组的大小-1 次 的循环

(2)每一趟排序的次数在逐渐的减少

(3) 如果我们发现在某趟排序中,没有发生一次交换, 可以提前结束冒泡排序。这个就是优化 

 选择排序(SelectSort):

选择排序(SelectSort)的基本思路:

1. 选择排序一共有 数组大小 - 1 轮排序

2. 每1轮排序,又是一个循环, 循环的规则(代码)

2.1先假定当前这个数是最小数

2.2 然后和后面的每个数进行比较,如果发现有比当前数更小的数,就重新确定最小数,并得到下标

2.3 当遍历到数组的最后时,就得到本轮最小数和下标

2.4 交换 [代码中再继续说 ]

 图解:

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第4张

代码详解:

import java.util.*; public class SelectSort { //selectsort排序的方法、 public static void selectSort(int[] arr){ for(int i=0;i<arr.length-1;i++){ int minIdex=i;//设第一个数为最小,同时将其索引(在数组中的位置)用minIndex表示 int min=arr[i];//用min记录最小的数 for(int j=i+1;j<arr.length;j++){ if(min>arr[j]){ min=arr[j];//通过选择将最小的数选出,然后更新min和minIndex minIdex=j; } } arr[minIdex]=arr[i];//将当前位置的数和最小数交换 arr[i]=min; System.out.println("这是第"+i+"次排序,结果是:");//记录排序的过程 System.out.println(Arrays.toString(arr)); } } //测试排序 public static void main(String[] args){ Scanner sc=new Scanner(System.in); int[] arr=new int[5]; System.out.println("请输入你要排序的数组:"); for(int i=0;i<5;i++){ arr[i]=sc.nextInt(); } selectSort(arr); System.out.println("排序好后的数组是:"); for(int i=0;i<5;i++){ System.out.print(arr[i]+" "); } } }

运行结果:(通过运行过程来展示排序过程)  

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第5张

 时间复杂度:最坏情况:O(N^2)
      最好情况:O(N^2)

空间复杂度:O(1)

 插入排序(InsertSort):

插入排序(InsertSort)基本思路:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素(数组的第一个元素),.无序表中包含n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序的排序码进行比较,将它插入到有序表中的适当位置,使其成为新的有序表

图解:

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第6张

代码详解:

import java.util.*; public class InsertSort { //InsertSort方法 public static void insertSort(int[] arr){ for(int i=1;i<arr.length;i++){ int insertVal=arr[i];//将无序的数组的第一个元素记录,是后面要插入的数据 int insertIndex=i-1;//同时有序的数组的最后一个元素的索引(元素在数组中的位置)记录 while(insertIndex>=0 && insertVal<arr[insertIndex]){//判断条件,二者要同时满足 arr[insertIndex+1]=arr[insertIndex];//将有序数组比insertVal小的元素后挪 insertIndex--; } arr[insertIndex+1]=insertVal;//将insertVal插入合适的位置 System.out.println("第"+i+"趟的排序为:");//记录排序过程 System.out.println(Arrays.toString(arr)); } } //排序测试 public static void main(String[] args){ Scanner sc=new Scanner(System.in); int[] arr=new int[5]; System.out.println("请输入要排序的数组:"); for(int i=0;i< arr.length;i++){ arr[i]=sc.nextInt(); } insertSort(arr); System.out.println("输出排好序的数组"); for(int i=0;i<arr.length;i++){ System.out.print(arr[i]+" "); } } }

 运行结果:(通过运行过程来展示排序过程)

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第7张

时间复杂度:最坏情况下为O(N*N),此时待排序列为逆序,或者说接近逆序
      最好情况下为O(N),此时待排序列为升序,或者说接近升序。

空间复杂度:O(1)

 希尔排序(ShellSort):

希尔排序(ShellSort)基本思路:希尔排序,先将待排序列进行预排序,使待排序列接近有序,然后再对该序列进行一次插入排序,此时插入排序的时间复杂度为O(N)

图解:

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第8张

 静态图解:

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第9张

 法一(交换法)代码详解:

import java.util.*; public class ShellSort { //ShellSort的方法 public static void shellSort(int[] arr){ int count=0; for(int gap= arr.length/2;gap>0;gap/=2){//将每次要排序的组别逐渐缩小 for(int i=gap;i<arr.length;i++){//gap~arr.length是该组别一共要交换的次数 for(int j=i-gap;j>=0;j-=gap){ if(arr[j]>arr[j+gap]){//通过交换对数组进行排序 int temp=arr[j]; arr[j]=arr[j+gap]; arr[j+gap]=temp; } } } System.out.println("第"+(++count)+"趟希尔排序是:"+Arrays.toString((arr)));//记录希尔排序的过程 } } //ShellSort的测试 public static void main(String[] args){ Scanner sc=new Scanner(System.in); int[] arr=new int[]{8,9,1,7,2,3,5,4,6,0}; System.out.println("排序前"+Arrays.toString((arr))); System.out.println(); shellSort(arr); System.out.println("排序后"+Arrays.toString(arr)); } }

运行结果:(通过运行过程来展示排序过程) 

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第10张

 法二(移位法-->插入排序的优化)代码详解:

import java.util.*; public class ShellSort2 { //对交换式的希尔排序进行优化->移位法 public static void shellSort2(int[] arr){ int count=0; // 增量gap, 并逐步的缩小增量 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]; j -= gap; } //当退出while后,就给temp找到插入的位置 arr[j] = temp; } } } }//shellSort2测试 public static void main(String[] args){ Scanner sc=new Scanner(System.in); int[] arr=new int[]{8,9,1,7,2,3,5,4,6,0}; System.out.println("排序前"+ Arrays.toString((arr))); System.out.println(); shellSort2(arr); System.out.println("排序后"+Arrays.toString(arr)); } }

运行结果: 

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第11张

时间复杂度平均:O(N^1.3)
空间复杂度:O(1)

快速排序(QuickSort): 

快速排序(QuickSort)的基本思路:

1.选出一个数据(一般是最左边或是最右边的)存放在temp变量中,在该数据位置形成一个坑
2、还是定义一个l和一个R,L从左向右走,R从右向左走。(若在最左边挖坑,则需要R先走;若在最右边挖坑,则需要L先走) 

图解:

 (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第12张

代码详解:

import java.util.*; public class QuickSort { //QuickSort方法 public static void quickSort(int[] arr, int left, int right) { if(left < right){ return ; } int l = left; //左下标 int r = right; //右下标 //pivot 中轴值 int pivot = arr[(left + right) / 2]; int temp = 0; //临时变量,作为交换时使用 //while循环的目的是让比pivot 值小放到左边 //比pivot 值大放到右边 while (l < r) { //在pivot的左边一直找,找到大于等于pivot值,才退出 while (arr[l] < pivot) { l += 1; } //在pivot的右边一直找,找到小于等于pivot值,才退出 while (arr[r] > pivot) { r -= 1; } //如果l >= r说明pivot 的左右两的值,已经按照左边全部是 //小于等于pivot值,右边全部是大于等于pivot值 if (l >= r) { break; } //交换 temp = arr[l]; arr[l] = arr[r]; arr[r] = temp; //如果交换完后,发现这个arr[l] == pivot值 相等 r--, 前移 if (arr[l] == pivot) { r -= 1; } //如果交换完后,发现这个arr[r] == pivot值 相等 l++, 后移 if (arr[r] == pivot) { l += 1; } } // 如果 l == r, 必须l++, r--, 否则为出现栈溢出 if (l == r) { l += 1; r -= 1; } //向左递归 quickSort(arr, left, r); //向右递归 quickSort(arr, l, right); } public static void main(String[] args) { Scanner sc = new Scanner(System.in);//测试排序 int[] arr = new int[5]; System.out.println("请输入你要排序的数组:"); for (int i = 0; i < 5; i++) { arr[i] = sc.nextInt(); } quickSort(arr, 0, arr.length - 1); System.out.println("排序好后的数组是:"); for (int i = 0; i < 5; i++) { System.out.print(arr[i] + " "); } } }

运行结果:

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第13张

 时间复杂度平均:O(N^LogN)
空间复杂度:O(LogN)

 归并排序(MergetSort):

 归并排序(MergetSort)基本思路:

该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。代价是需要额外的内存空间。若将两个有序表合并成一个有序表,称为2-路归并。

 图解:

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第14张

动态图解: 

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第15张

代码详解:

import java.util.Arrays; public class MergetSort { //分+合方法 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; // 指向temp数组的当前索引 //(一) //先把左右两边(有序)的数据按照规则填充到temp数组 //直到左右两边的有序序列,有一边处理完毕为止 while (i <= mid && j <= right) {//继续 //如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素 //即将左边的当前元素,填充到 temp数组 //然后 t++, i++ if(arr[i] <= arr[j]) { temp[t] = arr[i]; t += 1; i += 1; } else { //反之,将右边有序序列的当前元素,填充到temp数组 temp[t] = arr[j]; t += 1; j += 1; } } //(二) //把有剩余数据的一边的数据依次全部填充到temp while( i <= mid) { //左边的有序序列还有剩余的元素,就全部填充到temp temp[t] = arr[i]; t += 1; i += 1; } while( j <= right) { //右边的有序序列还有剩余的元素,就全部填充到temp temp[t] = arr[j]; t += 1; j += 1; } //(三) //将temp数组的元素拷贝到arr //注意,并不是每次都拷贝所有 t = 0; int tempLeft = left; // //第一次合并 tempLeft = 0 , right = 1 // tempLeft = 2 right = 3 // tL=0 ri=3 //最后一次 tempLeft = 0 right = 7 while(tempLeft <= right) { arr[tempLeft] = temp[t]; t += 1; tempLeft += 1; } } public static void main(String[] args){ int[] arr={56,78,13,78,33}; int[] temp=new int[arr.length]; System.out.println("排序前:"+ Arrays.toString(arr)); mergeSort(arr, 0, arr.length - 1, temp); System.out.println("排序后:"+Arrays.toString(arr)); } }

运行结果:

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第16张

  时间复杂度平均:O(N^LogN)
空间复杂度:O(N)

 基数排序(RadixSort):

基数排序(RadixSort)基本思路:

基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。

图解:

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第17张

代码详解:

import java.util.*; public class RadixSort { public static void radixSort(int[] arr) { //根据前面的推导过程,我们可以得到最终的基数排序代码 //1. 得到数组中最大的数的位数 int max = arr[0]; //假设第一数就是最大数 for (int i = 1; i < arr.length; i++) { if (arr[i] > max) { max = arr[i]; } } //得到最大数是几位数 int maxLength = (max + "").length(); //定义一个二维数组,表示10个桶, 每个桶就是一个一维数组 //说明 //1. 二维数组包含10个一维数组 //2. 为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr.length //3. 名明确,基数排序是使用空间换时间的经典算法 int[][] bucket = new int[10][arr.length]; //为了记录每个桶中,实际存放了多少个数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数 //可以这里理解 //比如:bucketElementCounts[0] , 记录的就是 bucket[0] 桶的放入数据个数 int[] bucketElementCounts = new int[10]; //这里我们使用循环将代码处理 for (int i = 0, n = 1; i < maxLength; i++, n *= 10) { //(针对每个元素的对应位进行排序处理), 第一次是个位,第二次是十位,第三次是百位.. for (int j = 0; j < arr.length; j++) { //取出每个元素的对应位的值 int digitOfElement = arr[j] / n % 10; //放入到对应的桶中 bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j]; bucketElementCounts[digitOfElement]++; } //按照这个桶的顺序(一维数组的下标依次取出数据,放入原来数组) int index = 0; //遍历每一桶,并将桶中是数据,放入到原数组 for (int k = 0; k < bucketElementCounts.length; k++) { //如果桶中,有数据,我们才放入到原数组 if (bucketElementCounts[k] != 0) { //循环该桶即第k个桶(即第k个一维数组), 放入 for (int l = 0; l < bucketElementCounts[k]; l++) { //取出元素放入到arr arr[index++] = bucket[k][l]; } } //第i+1轮处理后,需要将每个 bucketElementCounts[k] = 0 !!!! bucketElementCounts[k] = 0; } //System.out.println("第"+(i+1)+"轮,对个位的排序处理 arr =" + Arrays.toString(arr)); } } public static void main(String[] args){ Scanner sc = new Scanner(System.in);//测试排序 int[] arr = new int[5]; System.out.println("请输入你要排序的数组:"); for (int i = 0; i < 5; i++) { arr[i] = sc.nextInt(); } radixSort(arr); System.out.println("排序好后的数组是:"); for (int i = 0; i < 5; i++) { System.out.print(arr[i] + " "); } } }

 运行结果:

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第18张

   时间复杂度平均:O(N*K)
空间复杂度:O(N*K)

堆排序:

 可以看看我的这篇博客:十大经典排序算法之一--------------堆排序(java详解)-CSDN博客

最后,一张图概括: 

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第19张

博客到这里也是结束了,制作不易,喜欢的小伙伴可以点赞加关注支持下博主,这对我真的很重要~~

(java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序,堆排序】超详细~~,第20张 

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/sjk/13518727.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2024-02-27
下一篇 2024-02-29

发表评论

登录后才能评论

评论列表(0条)

保存