数据结构与算法之非比较排序【Java】
比较排序与非比较排序的对比
常见的快速排序、归并排序、堆排序、冒泡排序等属于比较排序。在排序的最终结果里,元素之间的次序依赖于它们之间的比较。每个数都必须和其他数进行比较,才能确定自己的位置。在冒泡排序之类的排序中,问题规模为n,又因为需要比较n次,所以平均时间复杂度为O(n²)。在归并排序、快速排序之类的排序中,问题规模通过分治法消减为logN次,所以时间复杂度平均O(nlogn)。比较排序的优势是,适用于各种规模的数据,也不在乎数据的分布,都能进行排序。可以说,比较排序适用于一切需要排序的情况。
计数排序、基数排序、桶排序则属于非比较排序。非比较排序是通过确定每个元素之前,应该有多少个元素来排序。针对数组arr,计算arr[i]之前有多少个元素,则唯一确定了arr[i]在排序后数组中的位置。
非比较排序只要确定每个元素之前的已有的元素个数即可,所有一次遍历即可解决。算法时间复杂度O(n)。非比较排序时间复杂度底,但由于非比较排序需要占用空间来确定唯一位置。所以对数据规模和数据分布有一定的要求。
非比较排序
常见的比较排序主要有以下几种:基数排序、计数排序、桶排序。
基数排序
1、基本思想:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
2、算法实现
1 package com.allSorts; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 /** 7 * Created by Demrystv. 8 */ 9 public class Ji1Shu { 10 public static void main(String[] args) { 11 int[] a = {23,45,67,88,90,21,42,52,73,61}; 12 System.out.println("排序前:"); 13 for(int i=0;i<a.length;i++){ 14 System.out.print(a[i]+" "); 15 } 16 17 //基数排序 18 sort(a); 19 20 System.out.println(""); 21 System.out.println("排序后:"); 22 for(int i=0;i<a.length;i++){ 23 System.out.print(a[i]+" "); 24 } 25 } 26 27 private static void sort(int[] a){ 28 29 //找到最大值,看最大值有几位,从而确定要比较几次,个十百千万。。。 30 int max = 0; 31 for(int i=0;i<a.length;i++){ 32 if(max<a[i]){ 33 max = a[i]; 34 } 35 } 36 37 //找到最大值后,确定要比较几次 38 int times = 0; 39 while (max>0){ 40 max = max/10; 41 times++; 42 } 43 44 //建立从0到9十个队列 45 List<ArrayList> list1 = new ArrayList<>(); 46 for(int i=0;i<10;i++){ 47 ArrayList list2 = new ArrayList(); 48 list1.add(list2); 49 } 50 51 //进行times次分配和收集,不断的重复即可。 52 for(int i=0;i<times;i++){ 53 54 //分配,按照比较的所在的位的大小将其放入list1 中,类似于数组链表中的链表 55 for(int j=0;j<a.length;j++){ 56 int x = a[j] % (int)Math.pow(10,i+1) / (int)Math.pow(10,i); 57 ArrayList list3 = list1.get(x); 58 list3.add(a[j]); 59 list1.set(x,list3); 60 } 61 62 //收集,然后以此从数组的从左到右,链表的从上到下进行收集,将其重新放进一个新的数组中 63 int count = 0 ; 64 for(int j=0;j<10;j++){ 65 while (list1.get(j).size()>0){ 66 ArrayList<Integer> list4 = list1.get(j); 67 a[count] = list4.get(0); 68 list4.remove(0); 69 count++; 70 } 71 } 72 } 73 74 75 } 76 }
3、分析
基数排序是稳定的排序算法。
基数排序的时间复杂度为O(k*n),空间复杂度为O(m),k为数组中数值的最大位数,m为桶的的个数。
计数排序
1、基本思想是:对每一个输入的元素arr[i],确定小于 arr[i] 的元素个数,可以直接把 arr[i] 放到它输出数组中的位置上。假设有x个数小于 arr[i],所以 arr[i] 应该放在数组的第(x+1)个位置上。
2、算法实现
1 package com.allSorts; 2 3 /** 4 * Created by Demrystv. 5 */ 6 public class Ji4Shu { 7 public static void main(String[] args) { 8 int[] a = {23,42,53,64,63,32,13,52,97,94,26}; 9 System.out.println("排序前:"); 10 for(int i=0;i<a.length;i++){ 11 System.out.print(a[i]+" "); 12 } 13 14 //计数排序 15 sort(a); 16 17 System.out.println(""); 18 System.out.println("排序后:"); 19 for(int i=0;i<a.length;i++){ 20 System.out.print(a[i]+" "); 21 } 22 } 23 24 private static void sort(int[] a){ 25 26 if(a==null || a.length==0){ 27 return; 28 } 29 30 int max=0,min=0; 31 for(int i=0;i<a.length;i++){ 32 max = Math.max(max,a[i]); 33 min = Math.min(min,a[i]); 34 } 35 36 int[] help = new int[max-min+1]; 37 int pos; 38 39 for(int i=0;i<a.length;i++){ 40 pos = a[i]-min; 41 help[pos]++; 42 } 43 44 int index=0; 45 for(int i=0;i<help.length;i++){ 46 while (help[i]>0){ 47 a[index] = i + min; 48 index++; 49 help[i]--; 50 } 51 } 52 } 53 }
3、分析
计数排序是稳定的排序算法。
计数排序的时间复杂度为O(k+n),空间复杂度为O(m),k为数组中最大数与最小数差值,m为桶的的个数。
桶排序
1、基本思想是把数组 arr 划分为n个大小相同子区间(桶),每个子区间各自排序,最后合并。计数排序是桶排序的一种特殊情况,可以把计数排序当成每个桶里只有一个元素的情况。
2、算法实现
1 package com.allSorts; 2 3 import java.util.ArrayList; 4 import java.util.Collections; 5 6 /** 7 * Created by Demrystv. 8 */ 9 public class Tong { 10 public static void main(String[] args) { 11 int[] a = {32,45,42,51,53,86,95,53,93,55,59}; 12 System.out.println("排序前:"); 13 for(int i=0;i<a.length;i++){ 14 System.out.print(a[i]+" "); 15 } 16 17 //桶排序 18 sort(a); 19 20 System.out.println(""); 21 System.out.println("排序后:"); 22 for(int i=0;i<a.length;i++){ 23 System.out.print(a[i]+" "); 24 } 25 } 26 27 private static void sort(int[] a){ 28 29 if(a==null || a.length==0){ 30 return; 31 } 32 33 int max=0,min=0; 34 for(int i=0;i<a.length;i++){ 35 max = Math.max(max,a[i]); 36 min = Math.min(min,a[i]); 37 } 38 39 //确定桶数,并且建立一系列的桶 40 int bucketNum = (max-min)/a.length+1; 41 ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum); 42 for (int i=0;i<bucketNum;i++){ 43 bucketArr.add(new ArrayList<Integer>()); 44 } 45 46 //将每个元素放入桶 47 for(int i=0;i<a.length;i++){ 48 int num = (a[i]-min)/a.length; 49 bucketArr.get(num).add(a[i]); 50 } 51 52 //对每个桶进行排序 53 for(int i=0;i<bucketArr.size();i++){ 54 Collections.sort(bucketArr.get(i)); 55 } 56 57 int index = 0; 58 for(int i=0;i<bucketArr.size();i++){ 59 while (bucketArr.get(i).size()>0){ 60 a[index++] = bucketArr.get(i).get(0); 61 bucketArr.get(i).remove(0); 62 } 63 } 64 } 65 }
3、分析
桶排序可用于最大最小值相差较大的数据情况,。
但桶排序要求数据的分布必须均匀,否则可能导致数据都集中到一个桶中,导致桶排序失效。