十大排序——11.十大排序的比较汇总及Java中自带的排序算法

这篇文章对排序算法进行一个汇总比较!

目录

0.十大排序汇总

0.1概述

0.2比较和非比较的区别

0.3基本术语

0.4排序算法的复杂度及稳定性

1.冒泡排序

算法简介

动图演示

代码演示

应用场景

算法分析

2.快速排序

算法简介

动图演示

代码演示

应用场景

算法分析

3.插入排序

算法简介

动图演示

代码演示

应用场景

算法分析

4.选择排序

算法简介

动图演示

代码演示

应用场景

算法分析

5.归并排序

算法简介

图片演示

代码演示

应用场景

算法分析

6.希尔排序

算法简介

图片演示

代码演示

应用场景

算法分析

7.堆排序

算法简介

图片演示

代码演示

应用场景

算法分析

8.计数排序

算法分析

动图演示

代码演示

应用场景

算法分析

9.基数排序

算法分析

动图演示

代码演示

应用场景

算法分析

10.桶排序

算法分析

动图演示

代码演示

应用场景

算法分析

11.Java自带的排序算法


0.十大排序汇总

0.1概述

一图以蔽之:

0.2比较和非比较的区别

常见的快速排序、归并排序、堆排序、冒泡排序等属于比较排序。在排序的最终结果里,元素之间的次序依赖于它们之间的比较。每个数都必须和其他数进行比较,才能确定自己的位置。


冒泡排序之类的排序中,问题规模为 n,又因为需要比较 n 次,所以平均时间复杂度为O(n²)。

归并排序、快速排序之类的排序中,问题规模通过分治法消减为 logN 次,所以时间复杂度平均O(nlogn)。


比较排序的优势是:适用于各种规模的数据,也不在乎数据的分布,都能进行排序。可以说,比较排序适用于一切需要排序的情况。


计数排序、基数排序、桶排序则属于非比较排序。非比较排序是通过确定每个元素之前,应该有多少个元素来排序。针对数组 arr,计算 arr 之前有多少个元素,则唯一确定了 arr 在排序后数组中的位置。


非比较排序只要确定每个元素之前的已有的元素个数即可,所有一次遍历即可解决。算法时间复杂度O(n)。


非比较排序时间复杂度底,但由于非比较排序需要占用空间来确定唯一位置。所以对数据规模和数据分布有一定的要求。

0.3基本术语

稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定:如果a原本在b的前面,而a=b,排序之后a可能出现在b的后面;
内排序:所有的排序操作都在内存中完成;
外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
时间复杂度:一个算法执行所消耗的时间;
空间复杂度:运行完一个程序所需内存的大小。

0.4排序算法的复杂度及稳定性

1.冒泡排序

算法简介

1. 从左向右依次对比相邻元素,将较大值交换到右边;
2. 每一轮循环可将最大值交换到最左边
3. 重复1.2两个步骤,直至完成整个数组。

动图演示

代码演示

package Sorts;import java.util.Arrays;
//冒泡排序
public class MaoPaoSort {public static void main(String[] args) {int[] arr = new int []{5,9,2,7,3,1,10};maoPaoSort(arr);System.out.println(Arrays.toString(arr));}public static void maoPaoSort(int[]arr){for (int i = 0; i <arr.length ; i++) {for (int j = 0; j <arr.length-1 ; j++) {if (arr[j] > arr[j+1]){//这是比较相邻的元素int temp = arr[j];arr[j]=arr[j+1];arr[j+1]=temp;}}}}
}

应用场景

一般在学习的时候作为理解排序原理的时候使用,在实际应用中不会使用

算法分析

最佳情况:T ( n ) = O ( n )

最差情况:T ( n ) = O ( n^2 )

平均情况:T ( n ) = O ( n^2 )

2.快速排序

算法简介

  1. 选择一个元素赋值给中间元素temp,默认选择左边第一个元素,这样左边第一个元素的位置就空出来了;
  2. 先从最右边的元素开始依次跟temp比较大小,大于等于temp元素的原地不动,遇到小于temp元素的则终止循环,把该元素赋值到左侧空出来的位置,同时左侧索引值自增,这是该元素原来的位置就空出;
  3. 然后左侧元素开始依次跟temp比较大小,小于等于temp元素的原地不动,遇到大雨temp元素的则终止循环,把该元素赋值到右侧空出来的位置,同时右侧索引值自减;
  4. 依次循环2,3步,直至左侧索引等于右侧索引,则完成一轮循环,把哨兵赋值到该索引的位置。
  5. 在分别递归地对 temp 左右两侧的子数组进行1234步,直至递归子数组只有一个元素则排序完成

动图演示

代码演示

package Sorts;import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;
//快排
public class QuickSort {public static void main(String[] args) {int[] array = {16,13,7,15,28,11,9,32,22,19};int[] array1 = {4,2,1,3,2,4};System.out.println("排序前:"+ Arrays.toString(array1));sort(array1);System.out.println("排序后:"+ Arrays.toString(array1));}/*** 交换操作* */private static void swap(int[] array,int i,int j){int t = array[i];array[i] = array[j];array[j] = t;}/*** 排序的函数* */private static void sort(int[] array) {quick(array,0,array.length-1);}/*** 递归的函数* */private static void quick(int[] array, int left, int right) {//结束递归的条件,如果我们左边的索引大于或等于右边的索引了(最多只能等于,不可能大于),那说明就只有一个元素的了,那就不用递归了if (left>=right){return;}int p = partition4(array,left,right); //p代表基准点元素的索引quick(array,left,p-1); // 对基准点左边的区进行递归操作quick(array,p+1,right); // 对基准点右边的区进行递归操作}/*** 分区并进行比较然后排序的函数(这部分是核心代码)* 单边快排* */private static int partition1(int[] array, int left, int right) {int pv = array[right]; //基准点的值int i = left;int j = left;while (j<right){ //当j小于右边届的时候,j就要+1if (array[j]<pv){ //j找到比基准点小的值if (i!=j){swap(array,i,j);}/*** 这里多说一点* i与j都是从left开始的,初始指向是一致的,i找大的,j找小的* 进入这个判断,就是说明j找到小的了* 没进入这个判断,就是说明j没有找到小的,也就是说此时j指向的值大于等于基准值* 因为i与j的指向在初始时是一致的* 所以没进入这个判断时,i指向的值就比基准值大了* 所以i就找到了,不用+1了* 但是j没有找到* 所以j要+1* 这就是i++在里面;j++在外面的原因* 如果进入这个判断,即j找到小的了,此时i在哪?* i在j前面或者和i在同一个位置,看代码就能想清楚* 有没有可能j找到小的了,然后不走了,i没找到继续走,然后在j后面找到大的了?* 这种情况就没必要交换了,并且这种情况进不来这个判断* */i++;}j++;}swap(array,i,right); //交换基准点与i的位置,此时i记录的就是基准点的位置return i;}/*** 双边快排* */private static int partition2(int[] array, int left, int right) {int pv = array[left]; //基准点的值int i = left; //游标i,从最左边开始,找大的int j = right; //游标j,从最右边开始,找小的while (i < j){ //i<j的时候进行循环,一旦i=j或i>j,就要退出循环while (i < j && array[j] > pv){//找比基准点小的值,没找到j就--,一旦找到,就退出循环j--;}
//            while (i<j && array[i]<pv)while (i<j && array[i]<=pv){//找比基准点大的值,没找到i就++,一旦找到,就退出循环i++;}swap(array,i,j);//交换}swap(array,left,i);return i;}/*** 定义随机的基准点* */private static int partition3(int[] array, int left, int right) {int idx = ThreadLocalRandom.current().nextInt(right-left+1)+left;//生成范围内的随机数swap(array,idx,left);//交换随机数与left的值int pv = array[left];//基准点的值int i = left; //游标i,从最左边开始,找大的int j = right; //游标j,从最右边开始,找小的while (i < j){ //i<j的时候进行循环,一旦i=j或i>j,就要退出循环while (i < j && array[j] > pv){//找比基准点小的值,没找到j就--,一旦找到,就退出循环j--;}while (i<j && array[i]<=pv){//找比基准点大的值,没找到i就++,一旦找到,就退出循环i++;}swap(array,i,j);//交换}swap(array,left,i);return i;}/*** 改进后的算法* 为了处理重复的元素* */private static int partition4(int[] array, int left, int right) {int pv = array[left];//基准点的值int i = left+1; //游标i,从left+1开始,找大的int j = right; //游标j,从最右边开始,找小的while (i <= j){ //i<=j的时候进行循环,一旦i>j,就要退出循环,当i=j的时候也要进入循环while (i <= j && array[j] > pv){//找比基准点小或等于的值,没找到j就--,一旦找到,就退出循环j--;}while (i<=j && array[i] < pv){//找比基准点大或相等的值,没找到i就++,一旦找到,就退出循环i++;}if (i<=j){swap(array,i,j);//交换i++;j--;}}swap(array,left,j);return j;}
}

应用场景

算法分析

最佳情况:T ( n ) = O ( nlogn )

最差情况:T ( n ) = O ( n^2 )

平均情况:T ( n ) = O ( nlogn )

3.插入排序

算法简介

  1. 左边第一个元素可作为有序子数组;
  2. 从第二个元素开始,依次向前比较,大于该元素的则向右移一位,直到比该元素小的元素,插入其后;
  3. 依次向后推进,直至整个数组成为有序数组

动图演示

代码演示

package Sorts;import java.util.Arrays;
//插入排序
public class InsertSort {public static void main(String[] args) {int [] arr = new int[]{5,9,2,7,3,1,10};Insert2(arr);System.out.println(Arrays.toString(arr));}//递归版的方法public static void Insert1(int[]arr, int low){if (low == arr.length)return;int t = arr[low];int i = low-1;//从右向左找插入位置,如果比待插入元素大,则不断右移,空出插入位置while (i >= 0 && t <arr[i]){arr[i+1] = arr[i];i--;}//找到插入位置if (i+1 != low){arr[i+1] = t;}Insert1(arr,low+1);}public static void Insert2(int[]arr){for (int low = 1; low < arr.length-1 ; low++) {int t = arr[low];int i = low-1;//从右向左找插入位置,如果比待插入元素大,则不断右移,空出插入位置while (i >= 0 && t <arr[i]){arr[i+1] = arr[i];i--;}//找到插入位置if (i+1 != low){arr[i+1] = t;}}}
}

应用场景

如果大部分数据距离它正确的位置很近或者近乎有序?例如银行的业务完成的时间
如果是这样的话,插入排序是更好的选择。

算法分析

最佳情况:T ( n ) = O ( n )

最坏情况:T ( n ) = O ( n^2 )

平均情况:T ( n ) = O ( n^2 )

4.选择排序

算法简介

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

动图演示

代码演示

package Sorts;import java.util.Arrays;
//选择排序
public class ChooseSort {public static void main(String[] args) {int [] arr = new int [] {5,9,2,7,3,1,10};sort(arr);System.out.println(Arrays.toString(arr));}public static void sort(int []arr){for (int i = 0; i <arr.length ; i++) {//从第一个索引开始,遍历数组for (int j = i; j <arr.length ; j++) {//从当前索引位置开始,遍历后面的数组if (arr[j]<arr[i]){//后面的数组元素比它小,就交换他两的位置(这两元素不相邻)int temp = arr[i];arr[i]=arr[j];arr[j]=temp;}}}}
}

应用场景

当数据量较小的时候适用

算法分析

最佳情况:T ( n ) = O ( n^2 )

最差情况:T ( n ) = O ( n^2 )

平均情况:T ( n ) = O ( n^2 )

5.归并排序

算法简介

分:

  1. 一直分两组,分别对两个数组进行排序(根据上层对下层在一组的数据通过临时数组排序,再将有序数组挪回上层数组中)。
  2.  循环第一步,直到划分出来的“小组”只包含一个元素,只有一个元素的数组默认为已经排好序。

合:(合并时,站在上层合并下层(使组内有序))

  1. 将两个有序的数组合并到一个大的数组中。
  2. 从最小的只包含一个元素的数组开始两两合并。此时,合并好的数组也是有序的。最后把小组合成一个组。

图片演示

代码演示

package Sorts;import java.util.Arrays;
//归并排序
public class Merge {public static void main(String[] args) {int[] array = new int[]{13,56,2,8,19,34,29};System.out.println("排序前:"+ Arrays.toString(array));mergeSort(array,0,array.length-1);System.out.println("排序后:"+Arrays.toString(array));}public static void mergeSort(int[] array, int low, int height){if (low >= height)return;int mid = (low+height)>>>1;mergeSort(array,low,mid);mergeSort(array,mid+1,height);merge(array,low,mid,height);}public static void merge(int[] array,int low,int mid,int height){int[] ret = new int[height-low+1];int i = 0;//新数组的索引int s1 = low;//前一个分段的初始位置int s2 = mid+1;//后一个分段的初始位置while (s1<=mid && s2<=height){if (array[s1]<=array[s2]){//比较元素的大小ret[i++] = array[s1++];//赋值给新数组,然后索引++}else {ret[i] = array[s2];i++;s2++;}}while (s1<=mid){//将前半段剩下的全部赋值到新数组中ret[i++] = array[s1++];}while (s2<=height){//将后半段剩下的全部赋值到新数组中ret[i++] = array[s2++];}for (int j = 0; j < ret.length; j++) {//将新数组中的元素全部挪到原数组中array[j+low] = ret[j];}}
}

应用场景

内存空间不足的时候使用归并排序,能够使用并行计算的时候使用归并排序。

算法分析

最佳情况:T ( n ) = O ( n )

最差情况:T ( n ) = O ( nlogn )

平均情况:T ( n ) = O ( nlogn )

6.希尔排序

算法简介

  1. 首先选择一个步长值gap,以步长值为间隔把数组分为gap个子数组gap=length/2
  2. 对每个子数组进行插入排序;
  3. 逐步减小步长 gap = gap/2,重复对数组进行1,2 步骤;
  4. 当步长值减为1时,相当于对数组进行一次直接插入排序。

图片演示

代码演示

package Sorts;import java.util.Arrays;
//希尔排序
public class XiErSort {public static void main(String[] args) {int [] arr = new int []{5,9,2,7,3,1,10};xiEr(arr);System.out.println(Arrays.toString(arr));}public static void xiEr(int arr[]){for (int gap = arr.length/2; gap >0 ; gap /=2) {for (int low = gap; low <arr.length ; low++) {int t = arr[low];int i = low - gap;//自右向左找插入位置,如果比待插入的元素大,则不断右移,空出插入位置while (i >= 0 && t < arr[i]){arr[i+gap] = arr[i];i-=gap;}//找到插入位置if(i != low-gap){arr[i+gap] = t;}}}}
}

应用场景

相对于直接插入排序,希尔排序要高效很多,因为当gap 值较大时,对子数组进行插入排序时要移动的元素很少,元素移动的距离很大,这样效率很高;在gap逐渐减小过程中,数组中元素已逐渐接近排序的状态,所以需要移动的元素逐渐减少;当gap为1时,相当于进行一次直接插入排序,但是各元素已接近排序状态,需要移动的元素很少且移动的距离都很小。

算法分析

最佳情况:T ( n ) = O ( nlog2 n )

最坏情况:T ( n ) = O ( nlog2 n )

平均情况:T ( n ) = O ( nlog2 n )

7.堆排序

算法简介

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

堆排序可以说是一种利用堆的概念来排序的选择排序。

分为两种方法:

  • 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
  • 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

图片演示

步骤一 :构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。
  a.假设给定无序序列结构如下

此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。

此处必须注意,我们把 6 和 9 比较交换之后,必须考量 9 这个节点对于其子节点会不会产生任何影响?因为其是叶子节点,所以不加考虑;

但是,一定要熟练这种思维,写代码的时候就比较容易理解为什么会出现一次非常重要的交换了。

找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。

在真正代码的实现中,这时候4和9交换过后,必须考虑9所在的这个节点位置,因为其上的值变了,必须判断对其的两个子节点是否造成了影响,这么说不合适,实际上就是判断其作为根节点的那棵子树,是否还满足大根堆的原则,每一次交换,都必须要循环把子树部分判别清楚。

这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。

牢记上面说的规则,每次交换都要把改变了的那个节点所在的树重新判定一下,这里就用上了,4和9交换了,变动了的那棵子树就必须重新调整,一直调整到符合大根堆的规则为截。

此时,我们就将一个无序序列构造成了一个大顶堆。

步骤二 :将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。

将堆顶元素9和末尾元素4进行交换

这里,必须说明一下,所谓的交换,实际上就是把最大值从树里面拿掉了,剩下参与到排序的树,其实只有总结点的个数减去拿掉的节点个数了。所以图中用的是虚线。

重新调整结构,使其继续满足堆定义

再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.

后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序

代码演示

package Sorts;
//堆排
public class HeapSort {public static void main(String[] args) {int[] arr = {16, 7, 3, 20, 17, 8};heapSort(arr);for (int i : arr) {System.out.print(i + " ");}}/*** 创建堆*/private static void heapSort(int[] arr) {//创建堆for (int i = (arr.length - 1) / 2; i >= 0; i--) {//从第一个非叶子结点从下至上,从右至左调整结构adjustHeap(arr, i, arr.length);}//调整堆结构+交换堆顶元素与末尾元素for (int i = arr.length - 1; i > 0; i--) {//将堆顶元素与末尾元素进行交换int temp = arr[i];arr[i] = arr[0];arr[0] = temp;//重新对堆进行调整adjustHeap(arr, 0, i);}}/*** 调整堆* @param arr 待排序列* @param parent 父节点* @param length 待排序列尾元素索引*/private static void adjustHeap(int[] arr, int parent, int length) {//将temp作为父节点int temp = arr[parent];//左孩子int lChild = 2 * parent + 1;while (lChild < length) {//右孩子int rChild = lChild + 1;// 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点if (rChild < length && arr[lChild] < arr[rChild]) {lChild++;}// 如果父结点的值已经大于孩子结点的值,则直接结束if (temp >= arr[lChild]) {break;}// 把孩子结点的值赋给父结点arr[parent] = arr[lChild];//选取孩子结点的左孩子结点,继续向下筛选parent = lChild;lChild = 2 * lChild + 1;}arr[parent] = temp;}
}

应用场景

堆排序适合于数据量非常大的场合(百万数据)。
堆排序不需要大量的递归或者多维的暂存数组。这对于数据量非常巨大的序列是合适的。比如超过数百万条记录,因为快速排序,归并排序都使用递归来设计算法,在数据量非常大的时候,可能会发生堆栈溢出错误。
堆排序会将所有的数据建成一个堆,最大的数据在堆顶,然后将堆顶数据和序列的最后一个数据交换。接下来再次重建堆,交换数据,依次下去,就可以排序所有的数据。

算法分析

最佳情况:T ( n ) = O ( nlogn )

最差情况:T ( n ) = O ( nlogn )

平均情况:T ( n ) = O ( nlogn )

8.计数排序

算法分析

找出待排序的数组中最大和最小的元素
统计数组中每个值为 i 的元素出现的次数,存入数组C的第 i 项;
对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
反向填充目标数组:将每个元素i放在新数组的第C( i ) 项,每放一个元素就将C( i ) 减去1。

动图演示

代码演示

package Sorts;import java.util.Arrays;//计数排序
public class CountSort {public static void main(String[] args) {int[] arr = {5,1,1,1,3,0};System.out.println(Arrays.toString(arr));countSort1(arr);System.out.println(Arrays.toString(arr));}//数组中元素>=0private static void countSort1(int[] arr) {int max = arr[0];for (int i = 1; i < arr.length; i++) {if (arr[i] > max)max = arr[i];}int[] count = new int[max+1];for (int v : arr) { //原始数组的元素count[v]++;}int k = 0;for (int i = 0; i < count.length; i++) {// i 代表原始数组中的元素,count[i]代表出现次数while (count[i]>0){arr[k++] = i;count[i]--;}}}//不限制数组中元素的大小,可以有负数private static void countSort2(int[] arr) {//让数组中的最小值映射到数组的count[0]位置,最大值映射到count的最右侧int min = arr[0];int max = arr[0];for (int i = 1; i < arr.length; i++) {if (arr[i] > max)max = arr[i];if (arr[i] < min)min = arr[i];}int[] count = new int[max - min + 1];//原始数组元素 - 最小值 = count 索引for (int v : arr) { //原始数组的元素count[v - min]++;}int k = 0;for (int i = 0; i < count.length; i++) {// i+min 代表原始数组中的元素,count[i]代表出现次数while (count[i]>0){arr[k++] = i + min;count[i]--;}}}
}

应用场景

计数排序需要占用大量空间,它仅适用于数据比较集中的情况。比如 [0,100],[10000,19999] 这样的数据。

算法分析

最佳情况:T ( n ) = O ( n+k )

最差情况:T ( n ) = O ( n+k )

平均情况:T ( n ) = O ( n+k )

9.基数排序

算法分析

相比其它排序,主要是利用比较和交换,而基数排序则是利用分配和收集两种基本操作。基数排序是一种按记录关键字的各位值逐步进行排序的方法。此种排序一般适用于记录的关键字为整数类型的情况。所有对于字符串和文字排序不适合。

实现:将所有待比较数值(自然数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
基数排序的两种方式:
高位优先,又称为最有效键(MSD),它的比较方向是由右至左;
低位优先,又称为最无效键(LSD),它的比较方向是由左至右;

动图演示

代码演示

package Sorts;import java.util.Arrays;//基数排序
public class RadixSort {public static void main(String[] args) {int [] arr = new int[]{193,255,12,6,78,50,31,564};radixSort(arr);;System.out.println(Arrays.toString(arr));}public static void radixSort(int[] array) {if (array == null || array.length <= 1) {return;}int length = array.length;// 每位数字范围0~9,基为10int radix = 10;int[] aux = new int[length];int[] count = new int[radix + 1];// 以关键字来排序的轮数,由位数最多的数字决定,其余位数少的数字在比较高位时,自动用0进行比较// 将数字转换成字符串,字符串的长度就是数字的位数,字符串最长的那个数字也拥有最多的位数int x = Arrays.stream(array).map(s -> String.valueOf(s).length()).max().getAsInt();// 共需要d轮计数排序, 从d = 0开始,说明是从个位开始比较,符合从右到左的顺序for (int d = 0; d < x; d++) {// 1. 计算频率,在需要的数组长度上额外加1for (int i = 0; i < length; i++) {// 使用加1后的索引,有重复的该位置就自增count[digitAt(array[i], d) + 1]++;}// 2. 频率 -> 元素的开始索引for (int i = 0; i < radix; i++) {count[i + 1] += count[i];}// 3. 元素按照开始索引分类,用到一个和待排数组一样大临时数组存放数据for (int i = 0; i < length; i++) {// 填充一个数据后,自增,以便相同的数据可以填到下一个空位aux[count[digitAt(array[i], d)]++] = array[i];}// 4. 数据回写for (int i = 0; i < length; i++) {array[i] = aux[i];}// 重置count[],以便下一轮统计使用for (int i = 0; i < count.length; i++) {count[i] = 0;}}}//Description: 根据d,获取某个值的个位、十位、百位等,d = 0取出个位,d = 1取出十位,以此类推。对于不存在的高位,用0补private static int digitAt(int value, int d) {return (value / (int) Math.pow(10, d)) % 10;}
}

应用场景

算法分析

最佳情况:T ( n ) = O ( n * k )

最差情况:T ( n ) = O ( n * k )

平均情况:T ( n ) = O ( n * k )

10.桶排序

算法分析

桶排序(Bucket Sort)的原理很简单,它是将数组分到有限数量的桶子里。

假设待排序的数组a中共有N个整数,并且已知数组a中数据的范围[0, MAX)。在桶排序时,创建容量为MAX的桶数组r,并将桶数组元素都初始化为0;将容量为MAX的桶数组中的每一个单元都看作一个"桶"。
在排序时,逐个遍历数组a,将数组a的值,作为"桶数组r"的下标。当a中数据被读取时,就将桶的值加1。例如,读取到数组a[3]=5,则将r[5]的值+1。

动图演示

代码演示

package Sorts;import java.util.*;//桶排
public class BucketSort {public static void main(String[] args) {int [] arr = new int[]{193,255,12,6,78,50,31,564};bucketSort(arr);;System.out.println(Arrays.toString(arr));}public static void bucketSort(int[] array) {if (array == null || array.length <= 1) {return;}// 建立桶,个数和待排序数组长度一样int length = array.length;LinkedList<Integer>[] bucket = (LinkedList<Integer>[]) new LinkedList[length];// 待排序数组中的最大值int maxValue = Arrays.stream(array).max().getAsInt();// 根据每个元素的值,分配到对应范围的桶中for (int i = 0; i < array.length; i++) {int index = toBucketIndex(array[i], maxValue, length);// 没有桶才建立桶(延时)if (bucket[index] == null) {bucket[index] = new LinkedList<>();}// 有桶直接使用bucket[index].add(array[i]);}// 对每个非空的桶排序,排序后顺便存入临时的List,则list中已经有序)List<Integer> temp = new ArrayList<>();for (int i = 0; i < length; i++) {if (bucket[i] != null) {Collections.sort(bucket[i]);temp.addAll(bucket[i]);}}// 将temp中的数据写入原数组for (int i = 0; i < length; i++) {array[i] = temp.get(i);}}private static int toBucketIndex(int value, int maxValue, int length) {return (value * length) / (maxValue + 1);}/***public static void bucketSort(int arr[] ){//定义二维数组int [][] bucket = new int[10][arr.length];//定义记录桶中数据个数的数组int [] bucketElement = new int[10];for (int i = 0; i <arr.length ; i++) {int element = arr[i] % 10;bucket[element][bucketElement[element]] = arr[i];bucketElement[element]++;}int index = 0;for (int k = 0; k <bucketElement.length ; k++) {if (bucketElement[k]!=0){for (int l = 0; l <bucketElement [k] ; l++) {arr[index] = bucket[k][l];index++;}}bucketElement[k]=0;}}*/
}

应用场景

算法分析

桶排序最好情况下使用线性时间O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。


最佳情况:T ( n ) = O ( n+k )

最差情况:T ( n ) = O ( n+k )

平均情况:T ( n ) = O ( n^2 )

11.Java自带的排序算法

JDK7-13:

JDK14-20:

  • 其中TimSort是用归并 + 二分插入排序的混合排序算法
  • 值得注意的是从JDK8开始支持 Arrays.parallelSort 并行排序
  • 根据最新的提交记录来看JDK21可能会引入基数排序等优化

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

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

相关文章

2024年4月最新版GPT

2024年4月最新版ChatGPT/GPT4, 附上最新的使用教程。 随着人工智能技术的不断发展&#xff0c;ChatGPT和GPT4已经成为了人们日常生活中不可或缺的助手。2024年4月,OpenAI公司推出了最新版本的GPT4,带来了更加强大的功能和更加友好的用户体验。本文将为大家带来最新版GPT4的实用…

Day91:API攻防-接口安全SOAPOpenAPIRESTful分类特征导入项目联动检测

目录 API分类特征-SOAP&OpenAPI&RESTful API分类特征 API常见漏洞 API检测流程 API检测项目-Postman&APIKit&XRAY 工具自动化-SOAP - WSDL Postman 联动burpxray APIKit插件(可联动xray) 工具自动化-OpenApi - Swagger Postman 联动burpxray APIKit…

机器学习和深度学习--李宏毅(笔记与个人理解)Day17

Day 17Convolutional Neyral Network (CNN) 卷积神经网络一般都用在image 上面比较多一些&#xff0c;所以课程的例子大多数也都是image Image Classification the same size how about for pc? 这里对于tensor 张量这个概念&#xff0c;我还是比较奇怪&#xff0c;在我认为一…

JDK5.0新特性

目录 1、JDK5特性 1.1、静态导入 1.2 增强for循环 1.3 可变参数 1.4 自动装箱/拆箱 1.4.1 基本数据类型包装类 1.5 枚举类 1.6 泛型 1.6.1 泛型方法 1.6.2 泛型类 1.6.3 泛型接口 1.6.4 泛型通配符 1、JDK5特性 JDK5中新增了很多新的java特性&#xff0c;利用这些新…

5.10 mybatis之useActualParamName作用

文章目录 1. useActualParamNamefalse1.1 单个参数映射1.2 多个数1.3 param注解 2. useActualParamNametrue useActualParamName官方解释&#xff1a;允许使用方法签名中的名称作为语句参数名称。 为了使用该特性&#xff0c;你的项目必须采用 Java 8 编译&#xff0c;并且加上…

Linux内核之aligned用法实例(四十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

KaiwuDB CTO 魏可伟:AIoT,用行业定义数据库

4月12日&#xff0c;由中国 DBA 联盟&#xff08;ACDU&#xff09;与墨天轮社区联合主办的第十三届数据技术嘉年华&#xff08;DTC 2024&#xff09;于北京盛大召开。KaiwuDB CTO 魏可伟受邀发表《智创当下&#xff0c;KaiwuDB 从多模到 AI 的探索实践》主题演讲&#xff0c;向…

看图找LOGO,基于YOLOv8全系列【n/s/m/l/x】参数模型开发构建生活场景下的商品商标LOGO检测识别系统

日常生活中&#xff0c;我们会看到眼花缭乱的各种各样的产品logo&#xff0c;但是往往却未必能认全&#xff0c;正因为有这个想法&#xff0c;这里我花费了过去近两周的时间采集和构建了包含50种商品商标logo的数据集&#xff0c;基于YOLOv8全系列的参数模型开发构建了对应的检…

2016NOIP普及组真题 1. 金币

线上OJ&#xff1a; 一本通&#xff1a;http://ybt.ssoier.cn:8088/problem_show.php?pid1969 核心思想&#xff1a; 解法1、由于数据量只有 10000 天&#xff0c;估可以采用 模拟每一天 的方式。 #include <bits/stdc.h> using namespace std;int k 0;int main() {i…

地质灾害监测预警系统:科技守护,构筑智能预警屏障

随着全球气候变化和人为活动的加剧&#xff0c;地质灾害频繁发生&#xff0c;给人们的生命财产安全带来了严重威胁。为了降低地质灾害带来的损失&#xff0c;地质灾害监测预警系统应运而生。本文将为您详细介绍地质灾害监测预警系统的原理、功能以及在实际应用中的效果。 一、地…

如何从零开始创建React应用:简易指南

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…