首页 > 技术文章 > 排序算法(插入排序、选择排序、冒泡排序、shell排序、快速排序)

studyhomeshi 2021-03-25 17:08 原文

选择排序

选择排序就是遍历数组找到最小的元素,与第一个交换位置;第一个已经排好序了,而后从第二个开始往后遍历,找到最小的元素,与第二个元素交换位置。而后从第三个元素开始往后遍历比较......直至所有元素排好序。

选择排序不占用额外空间,时间复杂度为O(n2)。

 动图演示(图片来源于网络)

插入排序

 插入排序即从第二个元素开始往后遍历,遍历到的元素与前面的元素比较,如果比自己前一个元素小,就与前一个元素交换位置,又与自己前一个位置的元素比较,比前一个小就交换,直到前一个元素比自己大。又在原位置往后遍历。

插入排序最好的情况就是已经排好序的数组,时间复杂度为O(n);

插入排序最差的情况就是逆序的数组,时间复杂度为O(n2);

动图演示(图片来源于网络)

 

冒泡排序

冒泡排序就是每次遍历把最大的数放在最后面,时间复杂度为O(n2)

动图演示(图片来源于网络)

 

shell(希尔)排序

希尔排序是插入排序的优化。设置一个gap值(步长),根据步长将数组元素分组,i,i+gap,i+gap+gap....一组,将组内元素排序。然后将步长减小(减半),再分组排序。直到步长等于一,进行一次插入排序。

为什么最后进行一次插入排序,而说希尔排序是插入排序的优化呢?

因为插入排序的时间复杂度在O(n)—O(n^2)之间,插入排序和数组的有序性有关,如果是逆序则需要O(n^2),而希尔排序最后一次插入排序之前数组已经局部有序了,所以能提高效率,减少时间复杂度。

希尔排序的时间复杂度为O(n^1.3)—O(n^2)。

 

 

快速排序

设置两个变量 low、high,排序开始时:low=0,high=size-1。
整个数组找基准正确位置,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面

默认数组的第一个数为基准数据,赋值给key,即key=array[low]。
因为默认数组的第一个数为基准,所以从后面开始向前搜索(high–),找到第一个小于key的array[high],就将 array[high] 赋给 array[low],即 array[low] = array[high]。(循环条件是 array[high] >= key;结束时 array[high] < key)
此时从前面开始向后搜索(low++),找到第一个大于key的array[low],就将 array[low] 赋给 array[high],即 array[high] = array[low]。(循环条件是 array[low] <= key;结束时 array[low] > key)
循环 2-3 步骤,直到 low=high,该位置就是基准位置。
把基准数据赋给当前位置。
第一趟找到的基准位置,作为下一趟的分界点。
递归调用(recursive)分界点前和分界点后的子数组排序,重复2.2、2.3、2.4的步骤。
最终就会得到排序好的数组。

快速排序时间复杂度为:O(nlogn);

 

 代码展示:

  1 package sort;
  2 
  3 import java.util.Random;
  4 
  5 public class Sort {
  6 
  7     public static void main(String[] args) {
  8         // TODO Auto-generated method stub
  9         int arry[]= new int[10];
 10         
 11         getarry(arry);
 12         selectionsort(arry);
 13         System.out.println("选择排序:");
 14         printout(arry);
 15         
 16         System.out.println();
 17         System.out.println("插入排序:");
 18         getarry(arry);
 19         insertionsort(arry);
 20         printout(arry);
 21         
 22         System.out.println();
 23         System.out.println("Shell排序:");
 24         getarry(arry);
 25         shellsort(arry);
 26         printout(arry);
 27         
 28         System.out.println();
 29         System.out.println("快速排序:");
 30         getarry(arry);
 31         quicksort(arry,0,arry.length-1);
 32         printout(arry);
 33     }
 34     
 35     //数组输出
 36     public static void printout(int []arry){
 37         for(int i=0;i<arry.length;i++)
 38         {
 39             System.out.print(arry[i]+" ");
 40         }
 41     }
 42     
 43     //生成随机数组
 44     public static void getarry(int [] a){
 45         Random rand=new Random();
 46         for(int i=0;i<a.length;i++)
 47         {
 48             a[i]=rand.nextInt(100)+1;
 49         }
 50     }
 51     
 52     
 53     //交换
 54     public static void exch(int [] a,int i,int j) {
 55         int n=a.length;
 56         if(i<0 || j>n ||i>n||j<0) 
 57             System.out.println("error");
 58         int tem=0;
 59         tem=a[j];
 60         a[j]=a[i];
 61         a[i]=tem;
 62     }
 63     
 64     //选择排序
 65     public static void selectionsort(int [] a) {
 66         int n=a.length;
 67         for (int i=0;i<n;i++){
 68             for(int j=i+1;j<n;j++){
 69                 if(a[j]<a[i]){
 70                     exch(a,i,j);
 71                 }
 72             }
 73         }
 74     }
 75     
 76     //插入排序
 77     public static void insertionsort(int [] a) {
 78         int n=a.length;
 79         //int k=0;
 80         for(int i=1;i<n;i++){
 81             for(int j=i;j>0;j--){
 82                 /*
 83                  * if(a[j]<a[j-1] exch(a,j,j-1);  这种方式也能排序,但是计算复杂度更高
 84                  */
 85                 if(a[j]>a[j-1]) break;    //如果前一个数比自己小,就不用往前查找了,直接看后一个数。
 86                 exch(a,j,j-1);
 87             }
 88         }
 89     }
 90     
 91     //shell排序
 92     public static void shellsort(int [] a){
 93         int n=a.length;
 94         for(int gap=n/2;gap>0;gap/=2){
 95             for(int i=gap;i<n;i++){
 96                 for(int j=i;j-gap>=0;j-=gap){
 97                 /*    if(a[j-gap]>a[j]){
 98                         exch(a,j-gap,j);
 99                     }*/
100                 //    if(a[j-gap]<a[j]) continue;  
101                     if(a[j-gap]<a[j]) break;//如果这组前一个数比自己小,则不用进行交换。直接退出
102                     exch(a,j-gap,j);
103                 }
104             }
105         }
106     }
107     
108     //快速排序
109     public static void quicksort(int []a,int low,int high){
110         if(low<high){    //如果low小于high说明排好序了,递归边界
111         int i=low,j=high;
112         int key=a[i];    //key是基准数,取数组第一个数,即a[low];
113         while(i<j){        //当low<high才继续排序,若low>high说明该数组已经根据基准数分为两部分,需要再把key左右两边分为两部分排序
114             while(i<j && a[j]>=key) j--;    //从队尾找比key大的数,如果小于等于key,则j--;
115             if(i<j) a[i]=a[j];    //此时已经找到比key小的数,如果满足i<j,则将a[j]赋值给a[i];把比key小的数放到前面
116             while(i<j && a[i]<=key) i++;    //从队头开始找比key大的数,如果小于等于key,继续往后找即:i++
117             if(i<j)    a[j]=a[i];    //跳出上一个循环说明已经找到比key大的数,或i=j;若满足i<j,将a[i]赋值给a[j],把比key大的数放到后面;
118             }
119             a[i]=key;    //跳出循环说明i已经等于j,则将key放到中间位置,此时key前面都是比key小的数,key后面都是比key大的数。
120             quicksort(a,low,i-1);    //基准数前半部分,继续递归细分
121             quicksort(a,i+1,high);    //基准数后半部分,继续递归细分
122         }
123     }
124 }

 

推荐阅读