zoukankan      html  css  js  c++  java
  • 排序-Java

    排序方法

    平均情况

    最好情况

    最坏情况

    辅助空间

    稳定性

    冒泡排序

    O(n2)

    O(n)

    O(n2)

    O(1)

    稳定

    简单选择排序

    O(n2)

    O(n2)

    O(n2)

    O(1)

    不稳定

    直接插入排序

    O(n2)

    O(n)

    O(n2)

    O(1)

    稳定

    希尔排序

    O(nlogn)~ O(n2)

    O(n1,3)

    O(n2)

    O(1)

    不稳定

    堆排序

    O(nlogn)

    O(nlogn)

    O(nlogn)

    O(1)

    不稳定

    归并排序

    O(nlogn)

    O(nlogn)

    O(nlogn)

    O(n)

    稳定

    快速排序

    O(nlogn)

    O(nlogn)

    O(n2)

    O(nlogn)~ O(n)

    不稳定

    1.排序算法

      链接:http://blog.csdn.net/pi9nc/article/details/1222085

    1.1选择排序

      步骤:
          1、初始状态:无序区为R[1..n],有序区为空。
          2、第i趟排序(i=1,2,3...n-1)
               第i趟排序开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。
                  该趟排序从当前无序区中选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,
                  使R[1..i]和R分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。
          3、前n-1趟结束,数组有序化了

      代码:

     1 public void selectSort(List<Integer> as,int left,int right){
     2         int N = right-left+1;
     3         for(int i=left;i<N;i++){
     4             int temp = i;
     5             for(int j=i+1;j<N;j++){
     6                 if(as.get(temp)>as.get(j)){
     7                   temp=j;
     8                 }
     9             }
    10             if(temp!=i){
    11                 int t = as.get(i) ;
    12                 as.set(i,as.get(temp));
    13                 as.set(temp, t);
    14             }
    15         }
    16       
    17     }

      分析:
          复杂度:最优:O(n^2),最差:O(n^2),平均:O(n^2)
          额外空间:O(1)
          稳定性:不稳定

    1.2 插入排序

    步骤:
        1、从第一个元素开始,该元素可以认为已经被排序
        2、取出下一个元素,在已经排序的元素序列中从后向前扫描
        3、如果该元素(已排序)大于新元素,将该元素移到下一位置
        4、重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
        5、将新元素插入到该位置后
        6、重复步骤2~5

    代码:

     1 private void insertSort(List<Integer> as,int left,int right){  
     2         int N = right-left+1;
     3         for(int i=left;i<right+1;i++){
     4             int temp = as.get(i);
     5             int j=i;
     6             for(;j>left;j--){
     7                 if(temp<as.get(j-1)){
     8                     as.set(j, as.get(j-1));
     9                 }else break;
    10             }
    11             as.set(j, temp); 
    12         }
    13    }

      分析:
         复杂度:最优:O(n),最差:O(n^2),平均:O(n^2)
         额外空间:O(1)
         稳定性:稳定

    1.3 希尔排序

    描述:
        1、先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。
        2、所有距离为d1的倍数的记录放在同一个组中,在各组内进行直接插入排序。
        3、取第二个增量d2<d1重复上述的分组和排序,
        4、直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。

    增量序列:
        1.希尔增量:ht=[n/2] hk=[hk+1/2]
        2.Knuth增量
        3.Hibbard增量
        4.Sedgewick增量

    1.4 堆排序

    步骤:
          数组储存成堆的形式之后,第一次将A[0]与A[n - 1]交换,
          再对A[0…n-2]重新恢复堆。第二次将A[0]与A[n-2]交换,
          再对A[0…n-3]重新恢复堆,重复这样的操作直到A[0]与A[1]交换。
          由于每次都是将最小的数据并入到后面的有序区间,
          故操作完成后整个数组就有序了

    代码:

     1 private  void duiSort(List<Integer> as){
     2   for(int i=as.size()-1;i>0;i--){
     3        int  tmp = as.get(i);
     4        as.set(i, as.get(0));
     5        as.set(0, tmp);
     6        perDown(as ,0,i);
     7    }
     8 }
     9 //建堆
    10 private  void buildDui(List<Integer> as){
    11    for(int i = as.size()/2;i>=0;i--){
    12        perDown(as ,i,as.size()) ;
    13    }
    14 }
    15 //下沉
    16 private  void perDown(List<Integer> as ,int i,int n){
    17    int child;
    18    int tmp;
    19    for(tmp=as.get(i);2*i+1<n;i=child){
    20         child = 2*i+1;
    21         if(child!=n-1&&as.get(child)<as.get(child+1)){
    22              child++;
    23          }
    24         if(tmp<as.get(child)){
    25              as.set(i,as.get(child));
    26         }else break;
    27    }
    28    as.set(i,tmp);
    29 }

      分析:
          复杂度:最优:O(nlogn),最差:O(nlogn),平均:O(nlogn)
          额外空间:O(n)

    1.5 归并排序

      描述:
          1、Divide: 把长度为n的输入序列分成两个长度为n/2的子序列。
          2、Conquer: 对这两个子序列分别采用归并排序。
          3、Combine: 将两个排序好的子序列合并成一个最终的排序序列。

      代码:

     1 /**
     2 *归并排序,将输入数组array从start到end的数值进行排序
     3 *@param array 输入数值数组
     4 *@param start 须排序的起始位置
     5 *@param end 须排序的结束位置
     6 */
     7 /**
     8 *把长度为n的输入序列分成两个长度为n/2的子序列
     9 *对这两个子序列分别分割成两个一半的子序列,依次递归分割
    10 *当出现有序子序列时进行回溯,将两个分别有序的子序列进行合并
    11 **/
    12 public static void mergeSorting(int[] array , int start,int end){
    13     if(start<end){
    14         int center = (start+end)/2;
    15         mergeSorting(array ,start,center);
    16         mergeSorting(array,center+1,end);
    17         merge(array,start,center+1,end);
    18     }
    19 }
    20 /**
    21 *合并,将数组arrayd的start到center和center+1到end已经排序好的数值按顺序合并
    22 *@param array 输入数值数组
    23 *@param leftPos 须合并的起始位置
    24 *@param rightPos 须合并的右边起始位置
    25 *@param rightEnd 须合并的右边结束位置
    26 */
    27 /**
    28 *(1)将两部分的最小值进行比较,将小者放入输入数组中记(这一部分出现新的最小值),
    29 *(2)再重复(1)的步骤,
    30 *(3)当其中一部分完了,另一部分依次放入输出数组
    31 **/
    32 public int[] merge(int[] array , int leftPos,int rightPos,int rightEnd){
    33     int[] marray = new int[rightEnd-leftPos+1];
    34     int tmpLeftPos = leftPos;
    35     int tmpLeftEnd =rightPos-1;
    36 
    37     int i=0;
    38     while(tmpLeftPos<=tmpLeftEnd&&rightPos<=rightEnd){
    39         if(array[tmpLeftPos]<array[rightPos]){
    40             marray[i++]= array[tmpLeftPos++];
    41         }else{
    42             marray[i++]= array[rightPos++];
    43         }
    44     }
    45     while(tmpLeftPos<=tmpLeftEnd){
    46         marray[i++]= array[tmpLeftPos++];
    47     }
    48     while(rightPos<=rightEnd){
    49         marray[i++]= array[rightPos++];
    50     }
    51     for(int tmp:marray){
    52         array[leftPos++] = tmp;
    53     }
    54     return array;
    55 }

    1.6 快速排序

      基本思想:
             通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,
             则可分别对这两部分记录继续进行排序,以达到整个序列有序

      步骤:
            1、从数列中挑出一个元素,称为 "基准"(pivot),
            2、重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
                 在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
            3、递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

      代码:

     1   private void swapArray(List<Integer> as,int i,int j){
     2         int tmp = as.get(i);
     3         as.set(i, as.get(j));
     4         as.set(j, tmp);
     5     }
     6     //枢纽元的选择:(三数中值分割法)左端、右端、中心位置三个元素的中值
     7     //(1):选取a[left]、a[right]、a[center]进行比较;
     8     //(2):最大者放到a[right],最小者放到a[left],中间值放到a[right-1],a[right-1]的值放到a[center]
     9     //(3):i初始为left,j初始为right-1
    10     private Integer Median3(List<Integer> as,int left,int right){
    11       
    12         int center= (left+right)/2;
    13         if(as.get(center)<as.get(left)){swapArray(as,center,left);}
    14         if(as.get(right)<as.get(left)){swapArray(as,right,left);}
    15         if(as.get(right)<as.get(center)){swapArray(as,right,center);}
    16        
    17         swapArray(as,right-1,center);
    18         return as.get(right-1);
    19     }
    20     private void quickSort(List<Integer> as,int left,int right){
    21         int DING =10;
    22         if(left+DING<=right){
    23             int key =  Median3( as,left,right);
    24            
    25             int i =left,j=right-1;
    26            
    27             while(true){
    28                 while(as.get(++i)<key){}
    29                 while(as.get(--j)>key){}
    30                 if(i<j) swapArray( as,i,j);
    31                 else break;
    32             }
    33            
    34             swapArray( as,i,right-1);
    35            
    36             quickSort(as,left,i-1) ;
    37             quickSort(as,i+1,right) ;
    38            
    39         }else{
    40             //当数组元素较少时采用插入排(截止范围N=10)
    41             insertSort(as,left,right);
    42         }
    43     }

    1.7 桶排序

      描述:
            1、设置一个定量的数组当作空桶子。
            2、寻访串行,并且把项目一个一个放到对应的桶子去。
            3、对每个不是空的桶子进行排序。
            4、从不是空的桶子里把项目再放回原来的串行中。

    2.最长公共子序列

      http://www.cnblogs.com/huangxincheng/archive/2012/11/11/2764625.html    
      http://wenku.baidu.com/view/9494d24be45c3b3567ec8bf9.html

    (1)子序列不见得一定是连续的,子串要求一定是连续的;

    动态规划:
        第一步:先计算最长公共子序列的长度。
        第二步:根据长度,然后通过回溯求出最长公共子序列。
        现有两个序列X={x1,x2,x3,...xi},Y={y1,y2,y3,....,yi},
        设一个C[i,j]: 保存Xi与Yj的LCS的长度。    
        递推方程为:
            1. 当i=0||j=0,则C[i][j]=0;
            2. 当i,j>0&Xi=Yi,则C[i][j]=C[i-1][j-1]+1;
            3. 当i,j>0&Xi!=Yi,则C[i][j]=C[i-1][j]>C[i][j-1]?C[i-1][j]:C[i][j-1];
        采用一个标记函数Flag[i,j],当
            ①:C[i,j]=C[i-1,j-1]+1  时 标记Flag[i,j]="left_up";    (左上方箭头)
            ②:C[i-1,j]>=C[i,j-1]   时 标记Flag[i,j]="left";       (左箭头)
            ③: C[i-1,j]<C[i,j-1]    时 标记Flag[i,j]="up";         (上箭头)

    代码:

     1 /**
     2   *  
     3   * @param str1 
     4   * @param n1
     5   * @param str2
     6   * @param n2
     7   * @return
     8   */
     9   int[][] LCS;
    10   String[][] Flag;
    11   ArrayList<Integer> WN = new ArrayList<Integer>();
    12   ArrayList<Integer> WM= new ArrayList<Integer>();
    13   
    14   public void getLCS(String str1,int n1,String str2,int n2){
    15       char[] ctr1 = str1.toCharArray();
    16       char[] ctr2 = str2.toCharArray();
    17       LCS=new int[n1+1][n2+1];
    18       Flag=new String[n1+1][n2+1];
    19       for(int i=0;i<=n1;i++){
    20           for(int j=0;j<=n2;j++){
    21               if(i==0||j==0){
    22                   LCS[i][j]=0;
    23                  
    24               }else{
    25                   if(ctr1[i-1]==ctr2[j-1]){
    26                         LCS[i][j]=LCS[i-1][j-1]+1;
    27                         Flag[i][j] = "left_up";
    28                     }
    29                     else{
    30                         if(LCS[i-1][j]>=LCS[i][j-1]){
    31                             LCS[i][j]=LCS[i-1][j];
    32                             Flag[i][j] = "left";
    33                         }else{
    34                             LCS[i][j]=LCS[i][j-1];
    35                             Flag[i][j] = "up";
    36                         }
    37                     }
    38               }
    39           }
    40       } 
    41   }
    42   
    43   public void SubSequence(int i, int j){
    44     
    45       if(i==0||j==0){
    46           return ;
    47       }
    48       if("left_up".equals(Flag[i][j])){
    49          WN.add(i-1);
    50          WM.add(j-1);
    51          SubSequence(i - 1, j - 1);
    52       }else{
    53           if("up".equals(Flag[i][j])){
    54               SubSequence(i, j - 1);
    55           }else{
    56               SubSequence(i - 1, j);
    57           }
    58       }
    59   }
    60   public String getSubString(String str1,String str2){
    61       String str ="";
    62       for(int i=0;i<WN.size();i++){
    63           str = str+str1.charAt(WN.get(i));
    64           System.out.println(WN.get(i)+" "+WM.get(i));
    65       }
    66       System.out.println(str);
    67       return str;
    68   }

    3.字符串相似度

      http://www.cnblogs.com/huangxincheng/archive/2012/11/11/2765633.html

      跟“最长公共子序列”一样,我们采用一个二维数组来保存字符串X和Y当前的位置的最小编辑距离。
      现有两个序列X={x1,x2,x3,...xi},Y={y1,y2,y3,....,yi},
      设一个C[i,j]: 保存Xi与Yj的当前最小的LD。
          ①: 当 Xi = Yi 时,则C[i,j]=C[i-1,j-1];
          ②:当 Xi != Yi 时, 则C[i,j]=Min{C[i-1,j-1],C[i-1,j],C[i,j-1]}+1;
      最终我们的C[i,j]一直保存着最小的LD

      代码:

     1 public static int getLCS(String str1,int n1,String str2,int n2){
     2       char[] ctr1 = str1.toCharArray();
     3       char[] ctr2 = str2.toCharArray();
     4       int[][] LCS=new int[n1+1][n2+1];
     5       for(int i=0;i<=n1;i++){
     6           for(int j=0;j<=n2;j++){
     7               if(i==0||j==0){
     8                   LCS[i][j]=0;
     9               }else{
    10                   if(ctr1[i-1]==ctr2[j-1]){
    11                         LCS[i][j]=LCS[i-1][j-1];
    12                     }
    13                     else{
    14                         int tmp = LCS[i-1][j]>LCS[i][j-1]?LCS[i][j-1]:LCS[i-1][j];
    15                         LCS[i][j] = (tmp>LCS[i-1][j-1]?LCS[i-1][j-1]:tmp) +1;
    16                     }
    17               }
    18           }
    19       } 
    20       return LCS[n1][n2];
    21   }

    4.数组分割

      问题:无序、元素2N的正整数数组,将数组分割为两个元素个数为N的数组,使两个数组的和接近

    1.元素有负数;
    2.分割的数组不是N个而是任意个;
    3.两个数组:
      a.和接近;
      b.接近总和的一半;
      c.两个数组和的差为一定值。
    4.两个数组a、b,大小都为n,数组元素的值任意整形数,无序;
       要求:通过交换a、b数组中的元素,使[数组a元素的和]与[数组b元素的和]之间的差最小

     1 void shuzudis(int[] arry){
     2         int N = arry.length;
     3         int sum =0;
     4         int min=arry[0];
     5         //找出最小值
     6         for(int i=0;i<N;i++){
     7             min = min>arry[i]?arry[i]:min;
     8         }
     9         //如果有负值,则整体都加上减去最小值,进行正数化
    10         if(min<0){
    11             for(int i=0;i<N;i++){
    12                 arry[i]-=min;
    13                 sum+=arry[i];
    14             }
    15         }
    16         //isOK[i][v]表示任意i个元素之和是否能够为v
    17         //初始化
    18         boolean[][] isOK = new boolean[N+1][sum+1];
    19         for(int i=0;i<N+1;i++){
    20             for(int j=0;j<sum+1;j++){
    21                 isOK[i][j]=false;
    22             }
    23         }
    24         isOK[0][0]=true;
    25         //重点
    26         for(int i=0;i<N;i++){
    27             for(int j=i+1;j>0;j--){
    28                 for(int v=0;v<sum+1;v++){
    29                     if(v>=arry[i]&&isOK[j-1][v-arry[i]]){
    30                         isOK[j][v]=true;
    31                     }
    32                 }
    33             }
    34         }
    35         //显示
    36         for(int i=0;i<N+1;i++){
    37             for(int j=0;j<sum+1;j++){
    38                 System.out.print(isOK[i][j]+" ");
    39             }
    40             System.out.println();
    41         }
    42     }

    5.5/3数组分割

      问题:编写一个函数,传入一个int型数组,返回该数组能否分成两组,
            使得两组中各元素加起来的和相等,并且,所有5的倍数必须在
            其中一个组中,所有3的倍数在另一个组中(不包括5的倍数),
            能满足以上条件,返回true;不满足时返回fals

     1 import java.util.*;
     2 
     3 public class Main {  
     4     public static void main(String[] args) {
     5         Scanner sc = new Scanner(System.in);
     6         while(sc.hasNext()){
     7             int n = sc.nextInt();
     8             int sum1 = 0, sum2 = 0;
     9             int[] a = new int[n];
    10             int count = 0;
    11             for(int i =0;i<n;i++){
    12                 int tmp = sc.nextInt();
    13                 if(tmp%5==0){
    14                     sum1+=tmp;
    15                 }
    16                 else if(tmp%3==0)
    17                     sum2 += tmp;
    18                 else{
    19                     a[count++] = tmp;
    20                 }
    21             }
    22             int sum = Math.abs(sum1-sum2);//这里只考虑绝对值就可以了
    23             System.out.println(f(0,count,a,0,sum));
    24         }
    25     }
    26  
    27     public static boolean f(int i ,int n,int[] a,int result,int sum){
    28         if(i==n){
    29             return Math.abs(result) == sum;//绝对值相等就可以
    30         }
    31         else{
    32             return (f(i+1,n,a,result+a[i],sum)||f(i+1,n,a,result-a[i],sum));
    33         }
    34     }
  • 相关阅读:
    引用 Geoprocessing调用ArcToolBox工具使用总结
    MySQL权限改观何时生效
    Ubuntu下安装*.bin挨次
    创立初始的MySQL权限
    MySQL的日期和光阴范例
    MySQL存取节制, 阶段1:连接证明
    让Linux操作零碎可以Mount NTFS分区
    Citrix进级XenDesktop桌面虚拟化产物
    如何在两台MySQL数据库间完成同步
    MySQL的数字
  • 原文地址:https://www.cnblogs.com/ljb161108/p/7144527.html
Copyright © 2011-2022 走看看