排序算法

八大排序算法 详见http://blog.csdn.net/intheair100/article/details/48734563

http://blog.csdn.net/wangiijing/article/details/51485119

内部排序:在内存中,主要消耗时间复杂度,用比较次数衡量效率

外部排序:不断在内外存间移动,主要消耗空间复杂度,用读/写外村的次数来衡量效率

直接插入排序

 

java代码

1 public static void insertionSort(int[] arr) {
2     for (int i = 1; i < arr.length; i++) {
3         int j = i;
4         while (j > 0 && arr[j] < arr[j - 1]) {
5             swap(arr,j,j-1);
6             j--;
7         }
8     }
9 }

最好的情况下,arr[j] 总是 不小于 arr[j-1]的,此时不执行while循环,所以此时时间复杂度为 O(n)

最坏的情况下,每次都执行while循环,所以此时时间复杂度为O(n^2)

平均时间复杂度是O(n^2)  ,空间复杂度为 O(1),稳定

希尔排序

更清晰的图示见此。

初始元素序列            8 9 1 7 2 3 5 4 6 0

Step1(gap=n/2=5): 3 5 1 6 0 8 9 4 7 2    因为此时有[8,3] [9,5] [1,4] [7,6] [2,0]对每组进行直接插入排序

Step2(gap=5/2=2): 0 2 1 4 3 5 7 6 9 8    因为此时有[3,1,0,9,7] [5,6,8,4,2]

Step3(gap=2/2=1): 0 1 2 3 4 5 6 7 8 9

 1     public static void sort(int []arr){
 2         //增量gap,并逐步缩小增量
 3        for(int gap=arr.length/2;gap>0;gap/=2){
 4            //从第gap个元素,逐个对其所在组进行直接插入排序操作
 5            for(int i=gap;i<arr.length;i++){
 6                int j = i;
 7                while(j-gap>=0 && arr[j]<arr[j-gap]){
 8                    //插入排序采用交换法
 9                    swap(arr,j,j-gap);
10                    j-=gap;
11                }
12            }
13        }
14     }

最好的情况下,时间复杂度为 O(n)

最坏的情况下,时间复杂度为O(n^2)

平均时间复杂度是O(n^3/2)  ,空间复杂度为 O(1),不稳定

简单选择排序

初始元素序列:    [ 49  38  65  97  76  13  27 ]   比较次数

第一趟           :     13  [ 38  65  97  76  49  27 ]    n-1

第二趟           :     13    27 [ 65  97  76  49  38 ]   n-2

第三趟           :     13   27   38  [ 97 76  49  65 ]   n-3

第四趟           :      13   27   38   49 [76  97 65 ]   n-4

第五趟           :      13   27   38   49  65 [97  76]   n-5

第六趟           :      13   27   38   49  65   76  97   n-6

详见此。

 1 public void selectSort(int List[], int len)
 2 {
 3     //简单选择排序的循环
 4     for (int i = 0; i < len; i++) {
 5         int k = i;
 6         //一次排序过程,终止条件是 j 扫描到了最后一个记录处
 7         for (int j = i + 1; j <= len; j++) {
 8             if (List[j] < List[k]) {
 9                 k = j;
10             }
11         }
12         //扫描完毕,交换最值,先判断是否重复
13         if (i != k) {
14             //交换
15             List[i] = List[i] + List[k];
16             List[k] = List[i] - List[k];
17             List[i] = List[i] - List[k];
18         }// end of if
19     }//end of for
20 }

比较次数n(n-1)/2,不稳定

堆排序 

堆排序的步骤可以描述如下:

      1.构建大根堆。首先我们的原始数组一般情况下是不满足堆的条件,既然我们要可用大根段的性质进行排序,第一步当然是对原始数组进行处理,构建大根堆。

      2.根节点数据处理以及大根堆重构。构建了大根堆之后,根节点的数据是最大值,将该数值取出,对剩下的元素重构大根堆,这时根节点是剩下元素的最大值,取出。只要不断重复上述的操作,不断取出未排序元素的最大值,直到未排序的元素只剩一个,就完成了排序工作。

 详见 http://www.cnblogs.com/yonghao/p/5140395.html

java代码见 https://blog.csdn.net/kimylrong/article/details/17150475

时间复杂度:nlogn,不稳定

冒泡排序

 对相邻的元素进行两两比较,顺序相反则进行交换,这样,每一趟会将最小或最大的元素“浮”到顶端,最终达到完全有序

 1 /*
 2  * 冒泡排序
 3  */
 4 public class BubbleSort {
 5   public static void main(String[] args) {
 6     int[] arr={6,3,8,2,9,1};
 7     System.out.println("排序前数组为:");
 8     for(int num:arr){
 9       System.out.print(num+" ");
10     }
11     for(int i=0;i<arr.length-1;i++){//外层循环控制排序趟数
12       for(int j=0;j<arr.length-1-i;j++){//内层循环控制每一趟排序多少次
13         if(arr[j]>arr[j+1]){
14           int temp=arr[j];
15           arr[j]=arr[j+1];
16           arr[j+1]=temp;
17         }
18       }
19     } 
20     System.out.println();
21     System.out.println("排序后的数组为:");
22      for(int num:arr){
23        System.out.print(num+" ");
24      } 
25   }
26  }

如果我们的数据正序,只需要走一趟即可完成排序。所需的比较次数C和记录移动次数M均达到最小值,即:Cmin=n-1;Mmin=0;所以,冒泡排序最好的时间复杂度为O(n)

如果很不幸我们的数据是反序的,则需要进行n-1趟排序。每趟排序要进行n-i次比较(1≤i≤n-1),所以,最坏的时间复杂度是O(n^2)

平均时间复杂度为O(n^2)

稳定

快速排序

1、先从数列中取出一个数作为基准数

2、分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边

3、再对左右区间重复第二步,直到各区间只有一个数

 

图解详见https://blog.csdn.net/it_zjyang/article/details/53406764

 1 /**
 2  * 快速排序
 3  * @author IT_ZJYANG
 4  */
 5 public class QuickSort {
 6     
 7     /**
 8      * 将数组的某一段元素进行划分,小的在左边,大的在右边
 9      * @param a
10      * @param start
11      * @param end
12      * @return
13      */
14     public static int divide(int[] a, int start, int end){
15         //每次都以最右边的元素作为基准值
16         int base = a[end];
17         //start一旦等于end,就说明左右两个指针合并到了同一位置,可以结束此轮循环。
18         while(start < end){
19             while(start < end && a[start] <= base)
20                 //从左边开始遍历,如果比基准值小,就继续向右走
21                 start++;
22             //上面的while循环结束时,就说明当前的a[start]的值比基准值大,应与基准值进行交换
23             if(start < end){
24                 //交换
25                 int temp = a[start];
26                 a[start] = a[end];
27                 a[end] = temp;
28                 //交换后,此时的那个被调换的值也同时调到了正确的位置(基准值右边),因此右边也要同时向前移动一位
29                 end--;
30             }    
31             while(start < end && a[end] >= base)
32                 //从右边开始遍历,如果比基准值大,就继续向左走
33                 end--;
34             //上面的while循环结束时,就说明当前的a[end]的值比基准值小,应与基准值进行交换
35             if(start < end){
36                 //交换
37                 int temp = a[start];
38                 a[start] = a[end];
39                 a[end] = temp;
40                 //交换后,此时的那个被调换的值也同时调到了正确的位置(基准值左边),因此左边也要同时向后移动一位
41                 start++;
42             }    
43             
44         }
45         //这里返回start或者end皆可,此时的start和end都为基准值所在的位置
46         return end;
47     }
48 
49     /**
50      * 排序
51      * @param a
52      * @param start
53      * @param end
54      */
55     public static void sort(int[] a, int start, int end){
56         if(start > end){
57             //如果只有一个元素,就不用再排下去了
58             return;
59         } 
60         else{
61             //如果不止一个元素,继续划分两边递归排序下去
62             int partition = divide(a, start, end);
63             sort(a, start, partition-1);
64             sort(a, partition+1, end);
65         }
66             
67     }
68     
69 }

快速排序最“快”的地方在于左右两边能够快速同时递归排序下去,所以最优的情况是基准值刚好取在无序区的中间,这样能够最大效率地让两边排序,同时最大地减少递归划分的次数。此时的时间复杂度仅为O(NlogN)。
快速排序也有存在不足的情况,当每次划分基准值时,得到的基准值总是当前无序区域里最大或最小的那个元素,这种情况下基准值的一边为空,另一边则依然存在着很多元素(仅仅比排序前少了一个),此时时间复杂度为O(N*N)。

 平均时间复杂度O(NlogN),空间复杂度(N),不稳定

归并排序

归并排序分为两个过程:一是数组划分为两个子数组并分别进行排序,二是将两个已排序的子数组进行合并

详见http://www.cnblogs.com/yonghao/p/5143364.html

代码详见https://www.cnblogs.com/chengxiao/p/6194356.html

基数排序

 首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中

接下来将这些桶子中的数值重新串接起来,接着再进行一次分配,这次是根据十位数来分配

 基数排序是一个快速的稳定排序算法。其时间复杂度可以表示为O(Kn)。其中n就是待排序序列的元素个数,K是数字的位数

 java代码见https://www.cnblogs.com/dwj411024/p/5978821.html

各类排序算法的比较

详见http://blog.csdn.net/sysukehan/article/details/52661295

原文地址:https://www.cnblogs.com/Hyacinth-Yuan/p/8496335.html