赞
踩
这篇文章对排序算法进行一个汇总比较!
目录
一图以蔽之:
常见的快速排序、归并排序、堆排序、冒泡排序等属于比较排序。在排序的最终结果里,元素之间的次序依赖于它们之间的比较。每个数都必须和其他数进行比较,才能确定自己的位置。
在冒泡排序之类的排序中,问题规模为 n,又因为需要比较 n 次,所以平均时间复杂度为O(n²)。
在归并排序、快速排序之类的排序中,问题规模通过分治法消减为 logN 次,所以时间复杂度平均O(nlogn)。
比较排序的优势是:适用于各种规模的数据,也不在乎数据的分布,都能进行排序。可以说,比较排序适用于一切需要排序的情况。
计数排序、基数排序、桶排序则属于非比较排序。非比较排序是通过确定每个元素之前,应该有多少个元素来排序。针对数组 arr,计算 arr 之前有多少个元素,则唯一确定了 arr 在排序后数组中的位置。
非比较排序只要确定每个元素之前的已有的元素个数即可,所有一次遍历即可解决。算法时间复杂度O(n)。
非比较排序时间复杂度底,但由于非比较排序需要占用空间来确定唯一位置。所以对数据规模和数据分布有一定的要求。
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定:如果a原本在b的前面,而a=b,排序之后a可能出现在b的后面;
内排序:所有的排序操作都在内存中完成;
外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
时间复杂度:一个算法执行所消耗的时间;
空间复杂度:运行完一个程序所需内存的大小。
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 )
- 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就要+1
- if (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 )
- 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 )
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
- 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 )
分:
合:(合并时,站在上层合并下层(使组内有序))
- 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 )
- 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 )
堆排序(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 )
找出待排序的数组中最大和最小的元素;
统计数组中每个值为 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));
- }
-
- //数组中元素>=0
- private 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 )
相比其它排序,主要是利用比较和交换,而基数排序则是利用分配和收集两种基本操作。基数排序是一种按记录关键字的各位值逐步进行排序的方法。此种排序一般适用于记录的关键字为整数类型的情况。所有对于字符串和文字排序不适合。
实现:将所有待比较数值(自然数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
基数排序的两种方式:
高位优先,又称为最有效键(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,基为10
- int 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. 计算频率,在需要的数组长度上额外加1
- for (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 )
桶排序(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 )
JDK7-13:
JDK14-20:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。