java排序总结

java排序从大的分类来看,可以分为内排序和外排序:其中,在排序过程中只使用了内存的排序称为内排序;内存和外存结合使用的排序成为外排序。

下面讲的都是内排序。

内排序在细分可以这样分:

1、选择排序:直接选择排序,堆排序

2、交换排序:冒泡排序,快速排序

3、插入排序:直接插入排序,二分插入排序,希尔排序

4、归并排序

5、基数排序

是不是觉得这样分类,文字的看着不形象,我也画了一张分类图:

               


下面是实现源码:

  1. package sort;  
  2.   
  3. /**   
  4.  * Package: sort   
  5.  *   
  6.  * File: JavaSorts.java    
  7.  *   
  8.  * Copyright @ 2015 Corpration Name   
  9.  *    
  10.  */  
  11. public class JavaSorts {  
  12.   
  13.     /** 
  14.      * 希尔排序 
  15.      * @param array 
  16.      */  
  17.     public static void ShellSort(int[] array){  
  18.         int d = array.length;  
  19.         do {  
  20.             d /= 2;   //设置增量,通过设置增量来进行分组,分组后,每一组内采用直接插入排序  
  21.             OneShell(array, d);//一个增量对应一趟希尔排序  
  22.         } while (d > 1);  
  23.     }  
  24.       
  25.     /** 
  26.      * 一趟希尔排序 
  27.      * @param array 
  28.      * @param d 
  29.      */  
  30.     public static void OneShell(int[] array,int d){  
  31.           
  32.         for (int i = 0; i < array.length - d; i++) {  
  33.             int temp = array[i+d]; //array[i+d]的拷贝,每一次插入操作所以插入的值  
  34.             if (array[i] > array[i + d]) {  
  35.                 int j = i;  
  36.                 //此时分组为:j,j+d,j+2d,j+3d····,组内采用直接插入排序  
  37.                 while(j >= 0 && array[j] > temp){  
  38.                     array[j + d] = array[j];    //使用while循环寻找temp能够插入的位置,从右往左寻找,大于temp的往后移动  
  39.                     j -= d;  
  40.                 }  
  41.                 array[j + d] = temp;   //插入数据  
  42.             }  
  43.         }     
  44.     }  
  45.       
  46.     /** 
  47.      * 快速排序 
  48.      * @param array 
  49.      * @param l 
  50.      * @param r 
  51.      */  
  52.     public static void QuickSort(int[] array,int l,int r){  
  53.         if (l < r) {  
  54.             int i = l,j = r,temp = array[l];  
  55.             while(i < j){  
  56.                 while(i < j && array[j] > temp){  
  57.                     j--;         //从右边开始寻找比temp小的数  
  58.                 }  
  59.                 if(i < j){  
  60.                     array[i++] = array[j]; //找到后,将这个数赋值到i位置上,然后i+1,因为下一轮寻找比temp大的数,从i+1位置开始  
  61.                 }  
  62.                 while(i < j && array[i] < temp){  
  63.                     i++;          //从左边寻找比temp大的数  
  64.                 }  
  65.                 if(i < j){  
  66.                     array[j--] = array[i]; //找到后,将这个数赋值到j位置上,然后j-1,因为下一轮寻找比temp小的数从j-1位置开始  
  67.                 }  
  68.             }  
  69.             array[i] = temp;  //此时比temp小的数都移动到左边,比temp大的数都移动到了右边,最后将temp赋值到中间位置  
  70.               
  71.             QuickSort(array, l, i - 1); //对temp左边的数进行递归  
  72.             QuickSort(array, i + 1, r); //对temp右边的数进行递归  
  73.         }  
  74.     }  
  75.       
  76.     /** 
  77.      * 堆排序 
  78.      * @param array 
  79.      */  
  80.     public static void HeapSort(int[] array){  
  81.         for (int i = 0; i < array.length; i++) {  
  82.             BuildMaxHeap(array, array.length - 1 - i);  
  83.             Swap(array, 0, array.length - 1 - i);  
  84.         }  
  85.     }  
  86.     /** 
  87.      * 创建最大堆 
  88.      * @param array 
  89.      * @param lastOneIndex 
  90.      */  
  91.     public static void BuildMaxHeap(int[] array,int lastOneIndex){  
  92.           
  93.         for (int i = (lastOneIndex - 1)/2; i >= 0; i--) {  
  94.             int k = i;  
  95.             while(2*k + 1 <= lastOneIndex){  
  96.                 int bigIndex = 2*k + 1;   //bigIndex用于记录一个节点树中最大数的索引  
  97.                 if (bigIndex < lastOneIndex) {  //满足这个条件,说明堆中有array[2*k+2]这个节点  
  98.                     if (array[bigIndex] < array[bigIndex + 1]) {  
  99.                         bigIndex++;      //若满足这个条件,说明k节点下的右子节点大于左子结点,因而bigIndex加1  
  100.                     }  
  101.                 }  
  102.                   
  103.                 if (array[k] < array[bigIndex]) {  
  104.                     Swap(array, bigIndex, k); //若k节点小于它其中一个子节点,则与这个子节点交换值  
  105.                     k = bigIndex;  //交换完值后,此时k节点下面的bigIndex节点可能不满足堆的性质,所以赋值给k,重新进入下一轮while循环  
  106.                 }else {  
  107.                     break;//若k节点满足堆的性质,则直接跳出循环  
  108.                 }  
  109.             }  
  110.         }  
  111.           
  112.     }  
  113.       
  114.     /** 
  115.      * 交换array中array[a]、array[b] 
  116.      * @param array 
  117.      * @param a 
  118.      * @param b 
  119.      */  
  120.     public static void Swap(int[] array,int a,int b){  
  121.         if (a < array.length && b < array.length) {  
  122.             int temp = array[a];  
  123.             array[a] = array[b];  
  124.             array[b] = temp;  
  125.         }  
  126.     }  
  127.       
  128.     /** 
  129.      * 直接插入排序 
  130.      * @param array 
  131.      */  
  132.     public static void DirectInsertSort(int[] array){  
  133.         for (int i = 0; i < array.length - 1; i++) {  
  134.             int temp = array[i + 1];  
  135.             if (array[i] > array[i + 1]) {  
  136.                 int j = i;  
  137.                 while(j >= 0 && array[j] > temp){  
  138.                     array[j + 1] = array[j];  
  139.                     j--;  
  140.                 }  
  141.                 array[j + 1] = temp;  
  142.             }  
  143.         }  
  144.     }  
  145.       
  146.     /** 
  147.      * 二分插入排序 
  148.      * @param array 
  149.      */  
  150.     public static void BinaryInsertSort(int[] array){  
  151.         for (int i = 0; i < array.length - 1; i++) {  
  152.             int temp = array[i + 1];  //需要插入的数  
  153.             if(array[i] > array[i + 1]){  
  154.                 int l = 0;   //有序队列中左边标识  
  155.                 int r = i;   //有序队列中右边标识  
  156.                 while(l < r){  
  157.                     int mid = (l + r) / 2; //永远指向l->r中间的那个值,中间值与temp(需要插入的值)比较  
  158.                     if (array[mid] > temp) {  
  159.                         r--;              //通过while循环,二分折半搜索temp应该插入的位置  
  160.                     }else {  
  161.                         l++;  
  162.                     }  
  163.                 }  
  164.                 //运行到这里,l==r,即是temp应该插入的位置是array[l](或者array[r])  
  165.                 for (int j = i + 1; j > l; j--) {     
  166.                     array[j] = array[j - 1];  //将l -> i的数都往后移动一位  
  167.                 }  
  168.                 array[l] = temp;  //将temp插入到l位置  
  169.                   
  170.             }  
  171.                   
  172.             }  
  173.     }  
  174.     /** 
  175.      * 直接选择排序 
  176.      * @param array 
  177.      */  
  178.     public static void DirectSelectSort(int[] array){  
  179.         for (int i = 0; i < array.length - 1; i++) {  
  180.             int min = array[i];  
  181.             for (int j = i + 1; j < array.length; j++) {  
  182.                 if (array[j] < min) {  
  183.                     min = array[j];  
  184.                     array[j] = array[i];  
  185.                     array[i] = min;  
  186.                 }  
  187.             }  
  188.         }  
  189.     }  
  190.     /** 
  191.      * 冒泡排序 
  192.      * @param array 
  193.      */  
  194.     public static void BubbleSort(int[] array){  
  195.         int temp = 0;  
  196.         for (int i = 0; i < array.length; i++) {  
  197.             for (int j = 0; j < array.length - 1; j++) {  
  198.                 if (array[j] > array[j+1]) {  
  199.                     temp = array[j];  
  200.                     array[j] = array[j+1];    
  201.                     array[j+1] = temp;  
  202.                 }  
  203.             }  
  204.         }  
  205.     }  
  206.       
  207.     /** 
  208.      * 归并排序 
  209.      * @param array 
  210.      */  
  211.     public static void MergeSort(int[] array){  
  212.         int left = 0;  
  213.         int right = array.length - 1;  
  214.         MergeSortRecursive(array, left, right);  
  215.     }  
  216.     /** 
  217.      * 归并排序的递归方法 
  218.      * @param array 
  219.      * @param left 
  220.      * @param right 
  221.      */  
  222.     public static void MergeSortRecursive(int[] array,int left,int right){  
  223.         if (left >= right) {  
  224.             return;  //递归的停止判断,没有这个判断会报StackOverflowError  
  225.         }  
  226.         int mid = (left + right)/2;  
  227.         MergeSortRecursive(array, left, mid);  
  228.         MergeSortRecursive(array, mid+1, right);  
  229.         Merge(array, left, mid, right);  
  230.     }  
  231.       
  232.     /** 
  233.      * 归并排序中合并方法 
  234.      * @param array 
  235.      * @param left 
  236.      * @param mid 
  237.      * @param right 
  238.      */  
  239.     public static void Merge(int[] array,int left,int mid,int right){  
  240.         int r_left = mid + 1; //需要合并数组中右边数组第一个数索引  
  241.         int[] tempArray = new int[array.length];//一个临时数组,用于合并时暂时存储  
  242.         int newIndex = left;   //临时数组索引  
  243.         int tempLeft = left;   //合并完了以后,用于复制数组的索引  
  244.         while(left <= mid && r_left <= right){   //将部分的数放入到临时数组中  
  245.             if (array[left] < array[r_left]) {  
  246.                 tempArray[newIndex++] = array[left++];  
  247.             }else {  
  248.                 tempArray[newIndex++] = array[r_left++];  
  249.             }  
  250.         }  
  251.         while (left <= mid) {  
  252.             tempArray[newIndex++] = array[left++];  //将左边还剩余的数放入临时数组(若需要合并的左边还剩余数)  
  253.         }  
  254.           
  255.         while(r_left <= right){  
  256.             tempArray[newIndex++] = array[r_left++];//将右边还剩余的数放入临时数组(若需要和并的右边还剩余数)  
  257.         }  
  258.         while(tempLeft <= right){  
  259.             array[tempLeft] = tempArray[tempLeft++];  //将临时数组复制到array  
  260.         }  
  261.     }  
  262.       
  263.     /** 
  264.      * 基数排序 
  265.      * @param array 
  266.      */  
  267.     public static void RadixSort(int[] array){  
  268.         int bits = FindMaxBit(array);  //得到数组中最大的那个数的位数  
  269.         for (int i = 0; i < bits; i++) {  
  270.             OneBitSort(array, i+1);  //一位基数的排序  
  271.         }  
  272.     }  
  273.     /** 
  274.      * 一位基数的排序 
  275.      * @param array 
  276.      * @param bit 
  277.      */  
  278.     public static void OneBitSort(int[] array,int bit){  
  279.         int[] tempArray = new int[array.length];  
  280.         int index = 0;  
  281.         int tempIndex = 0;  
  282.         for (int i = 0; i < 10; i++) {  
  283.             for (int j = 0; j < array.length; j++) {  
  284.                 if (FindBitNum(array[j], bit) == i) {  
  285.                     tempArray[index++] = array[j];  
  286.                 }  
  287.             }  
  288.         }  
  289.         while(tempIndex < array.length){  
  290.             array[tempIndex] = tempArray[tempIndex++]; //复制数组  
  291.         }  
  292.     }  
  293.     /** 
  294.      * 得到某个数某一位的数(例如:num=1234,n=2,FindBitNum(1234,2)=3) 
  295.      * @param num 
  296.      * @param n 
  297.      * @return 
  298.      */  
  299.     public static int FindBitNum(int num,int n){  
  300.         int key1 = (int)Math.pow(10, n);  
  301.         int key2 = (int)Math.pow(10, n-1);  
  302.         num %= key1;  
  303.         num /= key2;  
  304.         return num;  
  305.     }  
  306.     /** 
  307.      * 得到一个数组中最大数的位数 
  308.      * @param array 
  309.      * @return 
  310.      */  
  311.     public static int FindMaxBit(int[] array){  
  312.           
  313.         if (array == null || array.length ==0) {  
  314.             return 0;  
  315.         }  
  316.         int max = array[0];  
  317.         for (int i = 1; i < array.length; i++) {  
  318.             if (array[i] > max) {  
  319.                 max = array[i];  
  320.             }  
  321.         }  
  322.         int bit = 0;  
  323.         while(max / 10 != 0 || max % 10 != 0){  
  324.             max /= 10;  
  325.             bit++;  
  326.         }  
  327.         return bit;  
  328.           
  329.     }  
  330.     public static void main(String[] args) {  
  331.           
  332.         System.out.println("冒泡排序:"+SortThreads.getBubbleSortTime());  
  333.         System.out.println("直接选择排序:"+SortThreads.getDirSelectSortTime());  
  334.         System.out.println("直接插入排序:"+SortThreads.getDirInsertSortTime());  
  335.         System.out.println("二分插入排序:"+SortThreads.getBinaryInsertSortTime());  
  336.         System.out.println("快速排序:"+SortThreads.getQuickSortTime());  
  337.         System.out.println("希尔排序:"+SortThreads.getShellSortTime());  
  338.         System.out.println("归并排序:"+SortThreads.getMergeSortTime());  
  339.         System.out.println("基数排序:"+SortThreads.getRadixSortTime());  
  340.         System.out.println("堆排序:"+SortThreads.getHeapSortTime());  
  341.           
  342.           
  343.     }  
  344. }  

 

对于这些排序算法,我对它们的排序速度比较感兴趣,所以自己也测试了一下,数组是通过随机数产生的,我测的时候每一个数是不大于4位数,即不大于10000。

ProduceRandomNum.java产生随机数的数组:

  1. package sort;  
  2.   
  3. import java.util.Random;  
  4.   
  5. /**   
  6.  * Package: sort   
  7.  *   
  8.  * File: SortRunnable.java    
  9.  *   
  10.  * Copyright @ 2015 Corpration Name   
  11.  *    
  12.  */  
  13. public class ProduceRandomNum{  
  14.   
  15.     public synchronized static int[] RandomArray(int num,int n){  
  16.         Random random = new Random();  
  17.         int[] array = new int[num];  
  18.         for (int i = 0; i < array.length; i++) {  
  19.             array[i] = random.nextInt((int)Math.pow(10, n));  
  20.         }  
  21.         return array;  
  22.     }  
  23.       
  24.   
  25. }  


SortThreads.java测试排序的时间的方法,封装都一个类中,这个类我一直想把它写成多线程的方式(即是将这些排序一起运行),但是后面没有找到一个好的方法,所以后面就用了这种最笨的方法,有好办法的大神一定要指出来。

  1. package sort;  
  2. /**   
  3.  * Package: sort   
  4.  *   
  5.  * File: SortThreads.java    
  6.  *   
  7.  * Copyright @ 2015 Corpration Name   
  8.  *    
  9.  */  
  10. public class SortThreads {  
  11.   
  12.     private static final int arrayNum = 500000;  
  13.     private static final int bit = 4;  
  14.     public static long getBubbleSortTime(){  
  15.         long oldTime = System.currentTimeMillis();  
  16.         JavaSorts.BubbleSort(ProduceRandomNum.RandomArray(arrayNum, bit));  
  17.         long newTime = System.currentTimeMillis();  
  18.         return newTime - oldTime;  
  19.     }  
  20.     public static long getQuickSortTime(){  
  21.         int[] array = ProduceRandomNum.RandomArray(arrayNum, bit);  
  22.         long oldTime = System.currentTimeMillis();  
  23.         JavaSorts.QuickSort(array, 0, array.length - 1);  
  24.         long newTime = System.currentTimeMillis();  
  25.         return newTime - oldTime;  
  26.     }  
  27.     public static long getDirSelectSortTime(){  
  28.         long oldTime = System.currentTimeMillis();  
  29.         JavaSorts.DirectSelectSort(ProduceRandomNum.RandomArray(arrayNum, bit));  
  30.         long newTime = System.currentTimeMillis();  
  31.         return newTime - oldTime;  
  32.     }  
  33.     public static long getDirInsertSortTime(){  
  34.         long oldTime = System.currentTimeMillis();  
  35.         JavaSorts.DirectInsertSort(ProduceRandomNum.RandomArray(arrayNum, bit));  
  36.         long newTime = System.currentTimeMillis();  
  37.         return newTime - oldTime;  
  38.     }  
  39.       
  40.     public static long getBinaryInsertSortTime(){  
  41.         long oldTime = System.currentTimeMillis();  
  42.         JavaSorts.BinaryInsertSort(ProduceRandomNum.RandomArray(arrayNum, bit));  
  43.         long newTime = System.currentTimeMillis();  
  44.         return newTime - oldTime;  
  45.     }  
  46.       
  47.     public static long getShellSortTime(){  
  48.         long oldTime = System.currentTimeMillis();  
  49.         JavaSorts.ShellSort(ProduceRandomNum.RandomArray(arrayNum, bit));  
  50.         long newTime = System.currentTimeMillis();  
  51.         return newTime - oldTime;  
  52.     }  
  53.     public static long getMergeSortTime(){  
  54.         long oldTime = System.currentTimeMillis();  
  55.         JavaSorts.MergeSort(ProduceRandomNum.RandomArray(arrayNum, bit));  
  56.         long newTime = System.currentTimeMillis();  
  57.         return newTime - oldTime;  
  58.     }  
  59.     public static long getRadixSortTime(){  
  60.         long oldTime = System.currentTimeMillis();  
  61.         JavaSorts.RadixSort(ProduceRandomNum.RandomArray(arrayNum, bit));  
  62.         long newTime = System.currentTimeMillis();  
  63.         return newTime - oldTime;  
  64.     }  
  65.     public static long getHeapSortTime(){  
  66.         long oldTime = System.currentTimeMillis();  
  67.         JavaSorts.HeapSort(ProduceRandomNum.RandomArray(arrayNum, bit));  
  68.         long newTime = System.currentTimeMillis();  
  69.         return newTime - oldTime;  
  70.     }  
  71. }  


通过测试,我的测试数据(后面数据量大了,有的排序时间太长,我就用*代替了):


通过测试数据来看,不同排序适用于不同的场景,快速排序也不一定是最快的,在数据量比较小的时候,希尔排序反而更快,只是在不断增加数据量的时候,快速排序比较快也很明显。同时,排序也不能只看排序的速度,还需要看它的空间复杂度,即对内存空间利用的要求。例如,快排、归并、堆排序这三者,通过随机数产生数组,它们的时间复杂度可以说是基本一样的,但是当n比较大的时候,你会发现归并排序会比其他两个慢。

各种排序的时间复杂度、空间复杂度和稳定性,同样图形比较形象:



最后总结一下:

稳定性:
   1、稳定:冒泡排序、插入排序、归并排序和基数排序
   2、 不稳定:选择排序、快速排序、希尔排序、堆排序
平均时间复杂度
  1、O(n^2):直接插入排序,简单选择排序,冒泡排序。
        2、在数据量特别大的时候,冒泡排序基本是最慢的。
  3、在数据规模较小时(9W内),直接插入排序,简单选择排序差不多。当数据较大时,冒泡排序算法的时间代价最高。性能为O(n^2)的算法基本
上是相邻元素进行比较,基本上都是稳定的。
  1、O(nlogn):快速排序,归并排序,希尔排序,堆排序。
  2、其中,快排是最好的, 其次是归并和希尔,但数据量特别大的时候,归并排序很容易出现内存溢出的错误,如普通机器n>1000万时。
空间复杂度
         1、O(1):冒泡排序、直接插入排序、二分插入排序、希尔排序、直接选择排序、堆排序
         2、 O(n):归并排序
         3、 O(nlog2n):快速排序
         4、O(rd+n):基数排序
排序算法的选择
  1、数据规模较小
    (1)待排序列基本序的情况下,可以选择直接插入排序;
    (2)对稳定性不作要求宜用简单选择排序,对稳定性有要求宜用插入或冒泡
  2、数据规模不是很大
    (1)完全可以用内存空间,序列杂乱无序,对稳定性没有要求,快速排序,此时要付出log(N)的额外空间。
    (2)序列本身可能有序,对稳定性有要求,空间允许下,宜用归并排序
   3、数据规模很大
        (1)对稳定性有求,则可考虑归并排序。
        (2)对稳定性没要求,可以用快速排序。
  4、数组初始基本有序的时候,宜用直接插入排序,否则,可以用希尔排序。


posted @ 2017-06-29 16:06  大向无形  阅读(253)  评论(0编辑  收藏  举报