八大排序算法

来源:互联网 发布:阿里云服务器二级域名 编辑:程序博客网 时间:2024/06/04 20:12

  目前公认的排序算法有八种,如下:

  • 插入排序:直接插入排序、希尔排序
  • 选择排序:简单选择排序、堆排序
  • 交换排序:冒泡排序、快速排序
  • 归并排序
  • 基数排序
  本文目前主要讲述除堆排序之外的算法的原理和Java实现,就目前来看,个人认为百度百科上关于算法的描述和代码是比较适合初学者学习的。

   一、插入排序

   /**    * name:直接插入排序    * description:将一个数插入到一个已经排好序的序列中。对于数组,可以认为是按照如下描述进行的:首先是将第一个数字作为排好序的序列,然后将第二个数字与其比较,按照要求的    * 顺序进行插入;现在,前两个数字是排好序的了。下面,依次地进行第三次、第四次的排序即可。    * @param num    * @return    */   public int[] sort(int[] num) {       for(int i=1;i<num.length;i++) {//i为哨兵,代表了i之前的数字都是排好序的,此次的循环是为了将i位置的数字加入到排序中           if(num[i]<num[i-1]) {//表示需要进行插入               int temp=num[i];//把这个数字插入到正确的位置,即前一个数<=temp<后一个数               int j=i;//前面已经排好序的长度+1               while(j>0 && num[j-1]>temp) {//j>0很重要(而且必须放在前面),不然的话数组很可能会越界                   num[j] = num[j-1];//即j位置的数字后移,而j+1位置的数已经被保存了。                   j--;               }               //此时,num[j-1]<=temp<num[j]               num[j]=temp;           }       }       return num;   }

/** * name:希尔排序 * description:插入排序的一种,又称缩小增量排序。是直接插入排序的改进版本。 * 将记录按下标的一定增量分组,对每组使用直接插入排序;之后缩小增量,每组包括的 * 关键词越来越多;当增量减少为1时,整个数组恰好被分为一组,算法终止。 * method:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数 * 的记录放入同一组。先在各组内进行直接插入排序;然后取第二个增量d2<d1重复上述 * 排序,直到所取增量dt=1,即所有记录放在同一组中进行直接插入排序为止。 * ps:一般初次取序列的一半为增量,以后每次减半,直到增量为1 * @param num */public void shellSort(int[] num) {int d = num.length/2;while(d>=1) {eveShellSort(num,d);d=d/2;}}/** * 根据d的不同取值对num数组进行分组排序;将下标差值为d的数放入同一组 * @param num * @param d */public void eveShellSort(int[]num, int d) {for(int i=d;i<num.length;i++) {if(num[i]<num[i-d]) {//需要对当前数进行排序int thisNum=num[i];//需要进行直接插入的数字int j=i-d;//上一个数字while(j>=0 && num[j]>thisNum) {num[j+d]=num[j];//将前面的数后移d个位置(即下标差值为d)j=j-d;}num[j+d]=thisNum;//插入到正确的位置;注意此处最后插入的位置是j+d}}}

二、选择排序

/** * 简单选择排序 * 原理:在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换; * 然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换, * 依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。 * @param num *///升序public void simpleSelect(int[] num) {for(int i=1;i<num.length;i++) {//从第二个数开始,遍历数组剩下来的所有值,找到其中最小的,和第一个数字进行比较。//找到后面未遍历过的子序列中最小的那个值int minLoc=i;//defaultfor(int j=i+1;j<num.length;j++) {   if(num[j] < num[minLoc])   //尤其注意,此处是分别和num[minLoc]相比较,而不是相邻的两个数比较啥的   minLoc = j; }//swapif(num[i-1] > num[minLoc]) {int temp=num[i-1];num[i-1]=num[minLoc];num[minLoc]=temp;}}}

   关于堆排序的算法有机会会整理。


三、交换排序

 

/** * 冒泡排序 * @param num */public static void bubbleSortDESC(int[] num) {int size=num.length;boolean exchange=false;//true represent there exist data exchange last timeint k=0;for(int i=0;i<size;i++) {for(int j=0;j<size-i-1; j++) {if(num[j] > num[j+1]) {int temp=num[j+1];num[j+1]=num[j];num[j]=temp;exchange=true;}k++;}if(exchange==false) {break;}exchange=false;//reset}System.out.println("数组大小="+size+";循环次数="+k);}public static void bubbleSortPos(int[] num) {int k=0;int i=num.length;while(i>0) {int pos=0;for(int j=0;j<i-1; j++) {if(num[j] > num[j+1]) {int temp=num[j+1];num[j+1]=num[j];num[j]=temp;pos=j+1;}k++;}i=pos;//pos represent the last exchange position,so the data after pos don't need to be exchanged}System.out.println("数组大小="+num.length+";循环次数="+k);}

  下面是排序算法里面最重要的一个算法——快速排序,基本上很多公司面试都是考这个算法。

/** * 快速排序 * 算法描述:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按照此方法对两部分数据分别进行快速排序, * 整个排序过程可以用递归进行,以此达到整个数组变成有序队列。 *  * 详细描述:1)选择一个基准元素,通常是第一个元素或者最后一个元素 *       2)通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的元素值均比基准元素小,另一部分记录的元素值比基准值大。 *       3)此时基准元素在其排好序后的正确位置——即此元素无需再参加排序了 *       4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。 * 一趟快排描述: *       1)设置两个变量i、j,排序开始的时候:i=0,j=N-1 *       2)以第一个数组元素作为关键数据,赋值给key,即key=A[0] *       3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个比key小的值A[j],将A[i]与A[j]互换 *       4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个比key大的值A[i],将A[i]与A[j]互换 *       5)重复第3,4步,直到i==j(3、4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候j++,i++,直到找到为止)     * @param num * @param low  要排序的开始位置 * @param high 要排序的结束位置 */public static void quickSort(int[] num,int low,int high) {if(low>=high) return;//将num[low]放到合适的位置int i=low,j=high;int key=num[low];while(i<j) {//结束条件for(;j>i;j--) {if(num[j]<key) {int tmp=num[j];num[j]=num[i];num[i]=tmp;break;}}for(;i<j;i++) {if(num[i]>key) {int tmp=num[i];num[i]=num[j];num[j]=tmp;break;}}}quickSort(num,low,i);quickSort(num,i+1,high);}

四、归并排序

 /**   * 归并(merge)排序,是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,   * 每个子序列是有序的。然后再把有序子序列合并为整体有序序列。   * 分治法(Divide and Conquer)   *    * 具体描述:   * @param num   */  /**   * 归并排序主算法   * @param num 要进行排序的集合   * @param start 此次排序的开始位置   * @param thisLen 此次排序的元素长度   *    * 算法描述:通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。   */  public static void mergeSort(int[] num,int start,int thisLen) {  if(thisLen<=1) return;  if(thisLen==2) merge(num,start,start+1,start+1);  if(thisLen>2) {  mergeSort(num,start,thisLen/2);  mergeSort(num,thisLen/2+start,thisLen-thisLen/2);  merge(num,start,thisLen/2+start,thisLen+start-1);  }  }    /**   * 二路归并排序   * 算法描述:第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列                                   第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置                                   第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置                                  重复步骤3直到某一指针超出序列尾                                  将另一序列剩下的所有元素直接复制到合并序列尾   * @param num 要排序的集合的总体   * @param firStart 第一个有序表的开始位置   * @param secStart 第二个有序表的开始位置;注意第一个有序表的结束位置为secStart-1   * @param secEnd 第二个有序表的结束位置   */  public static void merge(int[] num, int firStart, int secStart,int secEnd) {  System.out.println("firStart="+firStart+"; secStart="+ secStart+"; secEnd="+secEnd);  int[] tmp = new int[secEnd-firStart+1];//用于存储二路归并排序后的结果  int i=firStart,j=secStart;//指向两个有序表的最初位置,然后分别向后移动  int k=0;//指向tmp中下一个空白的位置  while(i<secStart && j<=secEnd) {  if(num[i]<=num[j]) {  tmp[k]=num[i];  i++;  }else {  tmp[k]=num[j];  j++;  }  k++;  }  //下面把某一个还剩下元素的有序表中的元素复制到tmp中  if(i<secStart) {  while(i<secStart) {  tmp[k]=num[i];  i++;  k++;  }  }  if(j<=secEnd) {  while(j<=secEnd) {  tmp[k]=num[j];  j++;  k++;  }  }    //复制tmp中数据至原来的两个有序表位置  k=0;  i=firStart;  while(k<tmp.length) {  num[i]=tmp[k];  k++;  i++;  }    }


五、基数排序

/** * 基数排序 radix Sort * 概念:属于分配式排序distribution Sort,又称“桶子法”(bucket Sort)或bin sort, *   它是透过键值的部分资讯,将要排序的元素分配至某些“桶中”,藉以达到排序的作用。属于稳定性排序。 *    * 实现方法: *   1.最高位优先(Most significant digit first),简称MSD法:先找k1排序分组,同一组记录中 *   关键码k1相等,再对各组按k2排序分成子组,之后,对后面的关键码继续这样的排序分组,直到按最次位关键码 *   对各子组排序后。再将各组连接起来,便得到一个有序序列。 *   2.最低位优先(Least significant digit first),简称LSD法:先对kd开始排序,再对kd-1 *   进行排序,依次重复,直到对k1排序后便得到一个有序序列。 *    *   每一次分组排序的时候,按照对应位数字的大小分成0,1,2,3,4,5,6,7,8,9这几个组 * @param args *//** * 基数排序之LSD,即最低位优先 * 适用于位数较少的情况 * @param num * @param d */public static void radixSortLSD(int[] num, int d) {//d表示最大的数有几位int k=1;//表示第k次分组,即从右向左针对第k位的数进行分组int n=1;//表示每一次分组的时候,为了确定k位的数字而需要取模操作时,先用除法将其后面各位数字消除int[][] tmp = new int[10][num.length];//存储每一次分组排序的结果int[] groupNum=new int[10];//groupNum[i]存储某一次分组,该位为i的数字的个数,即存储在tmp第i行的数字的个数int m=0;//某一次排序的时候,指向num[m]while(k<=d) {for(int i=0;i<10;i++) {groupNum[i]=0;}for(int i=0;i<num.length;i++ ) {//对num数组的数字进行第k位的分组int tmpNum = (num[i]/n)%10;//当前这个数字在第k位的数tmp[tmpNum][groupNum[tmpNum]]=num[i];groupNum[tmpNum]++;//此次分组排序中,第tmpNum位的数字大小+1}//将tmp中的数字放回numfor(int i=0;i<10;i++) {for(int j=0;j<groupNum[i];j++) {//此次分组中每一组的数字个数num[m]=tmp[i][j];m++;}}k++;n=n*10;m=0;}}/** * 基数排序之最高位优先 * 适用于位数较多的情况 * @param num * @param d */public static void radixSortMSD(int[] num, int d) {//d表示最大的数有几位int k=d;int[][] tmp = new int[10][num.length];int[] groupNum=new int[10];//每一次分组的时候,当前组内的数字的个数int m=0;//指向每一次分组后再放回num数组的位置int kNumSize=0;int lastNumSize=0;while(k>=1) {for(int i=0;i<10;i++) {groupNum[i]=0;}for(int i=0;i<num.length-lastNumSize;i++) {int tmpNum = (num[i]/getMaxTen(k))%10;if(tmpNum>0) kNumSize++;//表示当前的这个数字num[i]是大于getMaxTen的,在下一个分组中就不会再计入了tmp[tmpNum][groupNum[tmpNum]]=num[i];groupNum[tmpNum]++;}//将tmp中的数字放回numfor(int i=0;i<10;i++) {for(int j=0;j<groupNum[i];j++) {//此次分组中每一组的数字个数num[m]=tmp[i][j];System.out.print(":"+num[m]);m++;}}System.out.println();k--;m=0;lastNumSize=kNumSize;//记录此次分组时,大于getMaxTen的数字的个数,它们的顺序已经确定了,无须再计入分组}}/** * 获得num数组中最大的那个数的位数n,10的n次方 * @return */static int getMaxTen(int d) {int k=1;int n=1;while(k<d) {n=n*10;k++;}return n; }public static void main(String[] args) {//int[] num = {73,22,93,243,5522,14,28,65,39,81,6,67};int[] num= {73,6256,93,14,28,65,39,81,6,67,243,5522};radixSortMSD(num,4);for(int i=0;i<num.length;i++) {System.out.print(num[i]+",");}System.out.println();System.out.print(getMaxTen(3));//for(int i=0;i<num.length;i++) {//int tmpNum = num[i]/getMaxTen(3);//System.out.println(tmpNum);;//}}



原创粉丝点击