数据结构之排序

Posted hzdzkjdxygz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构之排序相关的知识,希望对你有一定的参考价值。

1. 插入排序

1.1 直接插入排序  

       直接插入排序是将未排序的数据插入至已排好序序列的合适位置。
  具体流程如下:
  1、首先比较数组的前两个数据,并排序;
  2、比较第三个元素与前两个排好序的数据,并将第三个元素放入适当的位置;
  3、比较第四个元素与前三个排好序的数据,并将第四个元素放入适当的位置;
   ......
  4、直至把最后一个元素放入适当的位置。
 
  假如有初始数据:25  11  45  26  12  78。
  1、首先比较25和11的大小,11小,位置互换,第一轮排序后,顺序为:[11, 25, 45, 26, 12, 78]。
  2、对于第三个数据45,其大于11、25,所以位置不变,顺序依旧为:[11, 25, 45, 26, 12, 78]。
  3、对于第四个数据26,其大于11、25,小于45,所以将其插入25和45之间,顺序为:[11, 25, 26, 45, 12, 78]。
  .......
  4、最终顺序为:[11, 12, 25, 26, 45, 78]。
 
  直接插入排序是稳定的。直接插入排序的平均时间复杂度为O(n2)。
 
  Java 代码实现如下:
技术分享图片
 1     public void sort(int[] arr) {
 2         int tmp;
 3         for(int i = 1; i < arr.length; i++) {
 4             // 待插入数据
 5             tmp = arr[i];
 6             int j;
 7             for(j = i - 1; j >= 0; j--) {
 8                 // 判断是否大于tmp,大于则后移一位
 9                 if(arr[j] > tmp) {
10                     arr[j+1] = arr[j];
11                 }else{
12                     break;
13                 }
14             }
15             arr[j+1] = tmp;
16             System.out.println(i + ":" + Arrays.toString(arr));
17         }
18     }
技术分享图片

 1.2 希尔排序(最小增量排序)

希尔排序严格来说是基于插入排序的思想,又被称为缩小增量排序。

  具体流程如下:
  1、将包含n个元素的数组,分成n/2个数组序列,第一个数据和第n/2+1个数据为一对...
  2、对每对数据进行比较和交换,排好顺序;
  3、然后分成n/4个数组序列,再次排序;
  4、不断重复以上过程,随着序列减少并直至为1,排序完成。
    
  假如有初始数据:25  11  45  26  12  78。
  1、第一轮排序,将该数组分成 6/2=3 个数组序列,第1个数据和第4个数据为一对,第2个数据和第5个数据为一对,第3个数据和第6个数据为一对,每对数据进行比较排序,排序后顺序为:[25, 11, 45, 26, 12, 78]。
  2、第二轮排序 ,将上轮排序后的数组分成6/4=1个数组序列,此时逐个对数据比较,按照插入排序对该数组进行排序,排序后的顺序为:[11, 12, 25, 26, 45, 78]。
 
  对于插入排序而言,如果原数组是基本有序的,那排序效率就可大大提高。另外,对于数量较小的序列使用直接插入排序,会因需要移动的数据量少,其效率也会提高。因此,希尔排序具有较高的执行效率。
  希尔排序并不稳定,O(1)的额外空间,时间复杂度为O(N*(logN)^2)。
 
技术分享图片
 1     public void sort(int[] arr) {
 2         // i表示希尔排序中的第n/2+1个元素(或者n/4+1)
 3         // j表示希尔排序中从0到n/2的元素(n/4)
 4         // r表示希尔排序中n/2+1或者n/4+1的值
 5         int i, j, r, tmp;
 6         // 划组排序
 7         for(r = arr.length / 2; r >= 1; r = r / 2) {
 8             for(i = r; i < arr.length; i++) {
 9                 tmp = arr[i];
10                 j = i - r;
11                 // 一轮排序
12                 while(j >= 0 && tmp < arr[j]) {
13                     arr[j+r] = arr[j];
14                     j -= r;
15                 }
16                 arr[j+r] = tmp;
17             }
18             System.out.println(i + ":" + Arrays.toString(arr));
19         }
20     }
技术分享图片

2. 交换排序

2.1 冒泡排序

原理:比较两个相邻的元素,将值大的元素交换至右端。

思路:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。重复第一趟步骤,直至全部排序完成。

举例说明:要排序数组:int[] arr={6,3,8,2,9,1};   

第一趟排序:

    第一次排序:6和3比较,6大于3,交换位置:  3  6  8  2  9  1

    第二次排序:6和8比较,6小于8,不交换位置:3  6  8  2  9  1

    第三次排序:8和2比较,8大于2,交换位置:  3  6  2  8  9  1

    第四次排序:8和9比较,8小于9,不交换位置:3  6  2  8  9  1

    第五次排序:9和1比较:9大于1,交换位置:  3  6  2  8  1  9

    第一趟总共进行了5次比较, 排序结果:      3  6  2  8  1  9

---------------------------------------------------------------------

第二趟排序:

    第一次排序:3和6比较,3小于6,不交换位置:3  6  2  8  1  9

    第二次排序:6和2比较,6大于2,交换位置:  3  2  6  8  1  9

    第三次排序:6和8比较,6大于8,不交换位置:3  2  6  8  1  9

    第四次排序:8和1比较,8大于1,交换位置:  3  2  6  1  8  9

    第二趟总共进行了4次比较, 排序结果:      3  2  6  1  8  9

---------------------------------------------------------------------

第三趟排序:

    第一次排序:3和2比较,3大于2,交换位置:  2  3  6  1  8  9

    第二次排序:3和6比较,3小于6,不交换位置:2  3  6  1  8  9

    第三次排序:6和1比较,6大于1,交换位置:  2  3  1  6  8  9

    第二趟总共进行了3次比较, 排序结果:         2  3  1  6  8  9

---------------------------------------------------------------------

第四趟排序:

    第一次排序:2和3比较,2小于3,不交换位置:2  3  1  6  8  9

    第二次排序:3和1比较,3大于1,交换位置:  2  1  3  6  8  9

    第二趟总共进行了2次比较, 排序结果:        2  1  3  6  8  9

---------------------------------------------------------------------

第五趟排序:

    第一次排序:2和1比较,2大于1,交换位置:  1  2  3  6  8  9

    第二趟总共进行了1次比较, 排序结果:  1  2  3  6  8  9

---------------------------------------------------------------------

最终结果:1  2  3  6  8  9

---------------------------------------------------------------------

由此可见:N个数字要排序完成,总共进行N-1趟排序,每i趟的排序次数为(N-i)次,所以可以用双重循环语句,外层控制循环多少趟,内层控制每一趟的循环次数,即

技术分享图片

    public static void BubbleSort(int[] arr) {
        int temp;//定义一个临时变量
        for(int i=0;i<arr.length-1;i++){//冒泡趟数
            for(int j=0;j<arr.length-i-1;j++){
                if(arr[j+1]<arr[j]){
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }
技术分享图片

 2.2 快速排序

 

单单看以上解释还是有些模糊,可以通过实例来理解它,下面通过一组数据来进行排序过程的解析:

原数组:{3,7,2,9,1,4,6,8,10,5}

期望结果:{1,2,3,4,5,6,7,8,9,10}

 

花了点时间撸了下面这张快速排序示意图:

技术分享图片

Demo步骤解析:

1.一开始选定数组的最后一个元素5作为基准值,也就是最终排序结果应该是以5为界限划分为左右两边。

2.从左边开始,寻找比5大的值,然后与5进行调换(因为如果比5小的值本来就应该排在5前面,比5大的值调换之后就去到了5的后面),一路过来找到了7,将7与5调换,结束此次遍历。

3.从右边开始,由于7已经是上一轮排好序的便不再动它,从10开始,一路向左遍历,寻找比5小的值,然后与5进行调换(因为如果比5大的值本来就应该排在5后面,比5小的值调换之后就去到了5的后前面),一路过来找到了4,将4与5调换,结束此次遍历。

4.从左边开始,由于3和4都是前两轮已经排好序的便不再动它,从2开始,一路向右遍历,寻找比5大的值,然后与5进行调换(道理同步骤2),一路过来找到了9,将9与5调换,结束此次遍历。

5.从右边开始,由于排在9后面的那几个数字都是上几轮排好序的便不再动它,从1开始,一路向右遍历,寻找比5小的值,然后与5进行调换(道理同步骤3),一下子就找到了1,将1与5调换,结束此次遍历。

6.这个时候,发现5的左右两边都是排好序了的,所以结束此轮排序,5的左右两边抽出来各自进行下一轮的排序,规则同上,直到无法再拆分下去,即完成了整体的快速排序。

        Java代码
  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. }  
 
 
 

以上是关于数据结构之排序的主要内容,如果未能解决你的问题,请参考以下文章

初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段

初识Spring源码 -- doResolveDependency | findAutowireCandidates | @Order@Priority调用排序 | @Autowired注入(代码片段

面向面试编程代码片段之GC

数据结构-排序之基数排序(使用java代码实现)

数据结构-排序之基数排序(使用java代码实现)

数据结构-排序之基数排序(使用java代码实现)