• 排序算法


    八大排序算法 详见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

  • 相关阅读:
    STL与基础数据结构
    solr基础总结
    linux命令笔记
    SolrCloud集群部署
    oracle SQL笔记
    内存溢出之Tomcat内存配置
    solr查询优化(实践了一下效果比较明显)
    JAVA处理线程超时
    Solr 数据导入 <一>DIH简单使用
    几种防止表单重复提交的方法
  • 原文地址:https://www.cnblogs.com/Hyacinth-Yuan/p/8496335.html
Copyright © 2020-2023  润新知