zoukankan      html  css  js  c++  java
  • 线性排序算法-- 计数排序,基数排序,桶排序

     1 package sort;
     2 
     3 import java.util.Arrays;
     4 
     5 /**
     6  * 计数排序, 假设输入的数据是在某一个较小的范围内, 比如输入的数字为从0~k,
     7  * 时间复杂度为O(k+n), 当k=O(n)时, 时间复杂度为O(n), 但是需要额外的空间复杂度为O(n)
     8  * 属于非原址排序, 对于内存较为宝贵的排序不是很适合
     9  * 稳定排序
    10  */
    11 public class CountingSort
    12 {
    13     /**
    14      *
    15      * @param a : 待排序的数组
    16      * @param k : a内元素的个数为[0,k]
    17      */
    18     public static void sort(int[] a , int k)
    19     {
    20         int[] b = new int[a.length] ;
    21         countingSort(a , b , k+1) ;
    22     }
    23 
    24     private static void countingSort(int[] a , int[] b , int k)
    25     {
    26         int[] count = new int[k+1] ;
    27         for(int i=0 ; i<a.length ; i++)
    28         {
    29             count[a[i]+1]++ ;
    30         }
    31 
    32         for(int i=0 ; i<count.length-1 ; i++)
    33         {
    34             count[i+1] += count[i] ;
    35         }
    36 
    37         for(int i=0 ; i<a.length ; i++)
    38         {
    39             int pos = count[a[i]]++ ;
    40             b[pos] = a[i] ;
    41         }
    42 
    43         for(int i=0 ; i<a.length ; i++)
    44         {
    45             a[i] = b[i] ;
    46         }
    47     }
    48 
    49     public static void main(String[] args) {
    50         int[] a = new int[]{
    51                 6,0,2,0,1,3,4,6,1,3,2
    52         } ;
    53 
    54         sort(a , 6) ;
    55 
    56         System.out.println(Arrays.toString(a));
    57     }
    58 }

    基数排序是基于计数排序思想的一种排序算法, 根据从排序开始的顺序不同分为LSD(低位优先)和MSD(高位优先)

    LSD:

     1 package sort.radixSort;
     2 
     3 import java.util.Arrays;
     4 
     5 /**
     6  * 低位优先的基数排序, 对于位数一致的字符串或者数字进行排序比较有效
     7  * 对于给定n个d位数, 其中每一个数字有k种选择, 也就是每一种数字的范围是[0,k], 基数排序的时间复杂度为O(d*(n*k))
     8  * 当k=O(n)时, 基数排序的时间复杂度为O(n) ;
     9  * 额外需要O(n)的空间复杂度
    10  * 属于稳定排序, 非原址排序
    11  */
    12 public class LSD
    13 {
    14     /**
    15      *
    16      * @param a : a为待排序的数组
    17      * @param d : 最高位为d位, 第一位为最低位
    18      */
    19     public static void sort(int[] a , int d)
    20     {
    21         int[] b = new int[a.length] ;
    22         for(int time=0 ; time<d ; time++)
    23         {
    24             int[] count = new int[11] ; //0~9 一共10个数字+1
    25             for(int i=0 ; i<a.length ; i++)
    26             {
    27                 int pos = get(a[i] , time+1) ;
    28                 count[pos+1]++ ;
    29             }
    30 
    31             for(int i=0 ; i<count.length-1 ; i++)
    32             {
    33                 count[i+1] += count[i] ;
    34             }
    35 
    36             for(int i=0 ; i<a.length ; i++)
    37             {
    38                 int pos = get(a[i] , time+1) ;
    39                 int p = count[pos]++ ;
    40                 b[p] = a[i] ;
    41             }
    42 
    43             for(int i=0 ; i<a.length ; i++)
    44             {
    45                 a[i] = b[i] ;
    46             }
    47         }
    48     }
    49 
    50     //返回num的第k位, 最低位为第1位
    51     private static int get(int num , int k)
    52     {
    53         int n = (int)Math.pow(10 , k) ;
    54 
    55         return (num-num/n*n)*10/n ;
    56     }
    57     public static void main(String[] args)
    58     {
    59         int[] num = new int[]{
    60                 329 , 457 , 657 , 839 , 436 , 720 , 355
    61         } ;
    62 
    63         sort(num , 3) ;
    64 
    65         System.out.println(Arrays.toString(num));
    66     }
    67 }

    MSD:

    package sort.radixSort;
    
    import java.util.Arrays;
    
    /**
     * 高位优先的基数排序算法,
     * 主要性质和LSD类似, 唯一不同的地方在于MSD适用于键长不一样的数据排序
     * 而且MSD的性能主要依赖于输入的数据, 当输入的数据之间重复的公共部分很少时, 可以在线性时间内完成
     * 当输入的数据之间公共部分很多时, 性能会急速下降.
     * 当输入的数据每一位有k种选择, 那么对于输入N个数据, 时间复杂度约为Nlogr的N, 与键长无关
     * 对于有大量重复的情况, 可以通过在小数组切换排序算法进行处理.
     */
    public class MSD
    {
        private static int R = 256 ;
        private static String[] aux ;
        public static int charAt(String str , int i)
        {
            return i>= str.length() ? -1 : str.charAt(i)-97 ;
        }
        public static void sort(String[] str)
        {
            aux = new String[str.length] ;
            sort(str , 0 , str.length-1 , 0) ;
        }
    
        private static void sort(String[] str , int lo , int hi , int d)
        {
            if(lo >= hi)
                return ;
            int[] count = new int[R+2] ;//多一个-1表示这位不存在 再多1
    
            for(int i=lo ; i<=hi ; i++)
            {
                count[charAt(str[i] , d)+2]++ ;
            }
    
            for(int i=0 ; i<count.length-1 ; i++)
                count[i+1] += count[i] ;
            for(int i=lo ; i<=hi ; i++)
            {
                int pos = count[charAt(str[i] , d)+1]++ ;
                aux[pos] = str[i] ;
            }
    
            for(int i=lo ; i<=hi ; i++)
            {
                str[i] = aux[i-lo] ;
            }
    
            for(int i=0 ; i<count.length-1 ; i++)
                sort(str , lo+count[i] , lo+count[i+1]-1 , d+1) ;
        }
    
        public static void main(String[] args) {
    
            String[] str = new String[]{
                    "she" ,
                    "sells" ,
                    "seashells" ,
                    "by" ,
                    "the" ,
                    "sea" ,
                    "shore" ,
                    "the" ,
                    "shells" ,
                    "she" ,
                    "shel" ,
                    "sells" ,
                    "are" ,
                    "surely" ,
                    "seashells"
            } ;
    
            sort(str);
            System.out.println(Arrays.toString(str));
        }
    }

    桶排序: 网上很多资料的桶排序和基数排序相混淆, 实际上两种排序算法还是有区分的, 基数排序是把数据从高位到低位的顺序或者从低位到高位的顺序进行计数排序的, 而桶排序则是把数据按大小划分成若干个桶, 然后将每个桶按照某种排序算法排序, 然后依次取出形成最终的排序后的数据.

      1 package sort;
      2 
      3 import java.util.ArrayList;
      4 import java.util.Arrays;
      5 
      6 /**
      7  * 桶排序
      8  * 假设输入的数据服从均匀分布, 那么排序的平均时间复杂度为O(N)
      9  * 将[0,1)分成N个大小相同的区间, 将N个数据放入相应的区间(桶)中, 然后将各个桶进行排序, 最后合并n个桶
     10  * 算法导论里面是N个元素就N个桶, 我看网上博客具体桶的个数依具体情况而定.
     11  * 这里我的实现使用的是算法导论中的
     12  * 当输入的元素不满足均匀分布时, 只要所有桶的大小的平方与元素的个数呈线性关系,桶排序也可以在O(N)完成(这句话不是很理解,为什么桶的元素个数会固定呢)
     13  * 最坏情况下桶排序的时间复杂度取决于桶内使用的排序算法的时间复杂度, 如果桶内使用的是插入排序, 那么时间复杂度最坏为O(N^2)
     14  * 桶内使用的如果是快排, 那么时间复杂度为O(NlogN)
     15  * 非原址排序
     16  * 稳定性依赖于桶内的排序算法, 空间复杂度为O(N)
     17  */
     18 public class BucketSort
     19 {
     20     public static void sort(int[] a ,int maxNum)
     21     {
     22         int N = a.length ;
     23         ArrayList<Integer>[] bucket = new ArrayList[N] ;
     24 
     25         for(int i=0 ; i<bucket.length ; i++)
     26         {
     27             bucket[i] = new ArrayList<>() ;
     28         }
     29         for(int i=0 ; i<a.length ; i++)
     30         {
     31             int pos = a[i]/maxNum*N ;
     32             bucket[pos].add(a[i]) ;
     33         }
     34 
     35         int num = 0 ;
     36         for(int i=0 ; i<bucket.length ; i++)
     37         {
     38             Integer[] arr = bucket[i].toArray(new Integer[0]) ;
     39             quickSort(arr);
     40 
     41             for(int n : arr)
     42                 a[num++] = n ;
     43         }
     44 
     45     }
     46 
     47     private static void quickSort(Integer[] arr)
     48     {
     49         quickSort(arr , 0 , arr.length-1);
     50     }
     51 
     52     private static void quickSort(Integer[] arr , int lo , int hi)
     53     {
     54         if(lo >= hi)
     55             return ;
     56 
     57         int mid = partition(arr , lo , hi) ;
     58         quickSort(arr , lo , mid-1) ;
     59         quickSort(arr , mid+1 , hi) ;
     60     }
     61 
     62     private static int partition(Integer[] arr , int lo , int hi)
     63     {
     64         int mid = median(arr , lo , hi) ;
     65         int left = lo-1 ;
     66         int right = mid ;
     67 
     68         while (true)
     69         {
     70             while (arr[++left] < arr[mid])
     71                 ;
     72             while (arr[--right] > arr[mid])
     73                 ;
     74 
     75             if(left < right)
     76                 swap(arr , left ,right);
     77             else
     78                 break ;
     79         }
     80 
     81         swap(arr , left , mid);
     82 
     83         return left ;
     84     }
     85 
     86     private static int median(Integer[] arr , int lo , int hi)
     87     {
     88         int mid = lo+(hi-lo)/2 ;
     89         if(arr[lo] > arr[mid])
     90             swap(arr , lo , mid);
     91         if(arr[lo] > arr[hi])
     92             swap(arr , lo , hi) ;
     93         if(arr[mid] > arr[hi])
     94             swap(arr , mid , hi) ;
     95 
     96         swap(arr , mid , hi-1) ;
     97 
     98         return hi-1 ;
     99     }
    100 
    101     private static void swap(Integer[] arr , int p , int q)
    102     {
    103         int temp = arr[p] ;
    104         arr[p] = arr[q] ;
    105         arr[q] = temp ;
    106     }
    107 
    108     public static void main(String[] args)
    109     {
    110         int[] a = new int[]{
    111                 78 , 17 ,39, 26 ,72 , 94 , 21 , 12 , 23 , 68
    112         } ;
    113 
    114         sort(a , 100);
    115 
    116         System.out.println(Arrays.toString(a));
    117     }
    118 }

    //如果有什么不正确的地方,欢迎指出

  • 相关阅读:
    Codeforces Round #647 (Div. 2) E Johnny and Grandmaster
    Codeforces1363E Tree Shuffling
    洛谷P6583 回首过去
    Codeforces 817D Imbalanced Array
    Codeforces 213E Two Permutations
    hdu6312 Game
    树算法笔记(三):Prim最小生成树
    基础数学(四):充分与必要条件
    树算法笔记(二):Kruskal最小生成树
    树算法笔记(一):并查集
  • 原文地址:https://www.cnblogs.com/iamzhoug37/p/5585334.html
Copyright © 2011-2022 走看看