八大排序算法

  排序算法有内部排序和外部排序,内部排序就是数据记录在内存中进行排序,而外部排序是因排序的数据很多,一次不能容纳全部的排序数据,在排序的过程中需要访问外存。

  这里讲述的八大排序就是内部排序。

这里写图片描述

  当n较大时,则应采用时间复杂度为o(nlog2n)的排序方法-->即快速排序,堆排序或归并排序。

  快速排序:是当下内部排序中被认为最好的方法,当待排序的数据是随机分布时,快速排序的**平均时间**最短

1、插入排序–> 直接插入排序

2、插入排序–> 希尔排序

3、选择排序–> 简单选择排序

基本思想:在待排序的数组中,找出最小(或最大)的一个数与**第一个位置**上的数交换,然后在剩下的数中再找出最小(或最大)的一个数与**第二个位置**上的数交换,依次类推,直到第n-1个(倒数第二个)元素和第n个(最后一个)元素比较为止。

简单排序的例子:

这里写图片描述

结果演示的逻辑: 
  第一趟:从n条记录中找出最小记录与第一条记录交换。
  第二趟:从第二条记录开始的n-1条记录中找出最小记录与第一条记录交换。
  依次类推
  第i趟  :从第i条记录开始的n-i条记录开始找出最小的记录与第i条记录交换。
  直到所有的记录都排好序。

由结果演示可得到简单排序算法每一趟循环最多只有俩个位置的记录发生变化。这与下面要说的冒泡算法是不同的。

下面代码是基于上述思想的一种代码实现。自己理解不同代码结构不同。

public static void main(){
         //定义待排序的数组 4,1, 9,2,7,5,3,6
         int a[] ={4,1,9,2,7,5,3,6};
         //定义中间变量   用于俩个数字交换时的转换
         int tmp;
         for(int i = 0; i<a.length; i++){
             int min = i;  //  默认第i个位置的最小
             //找出从第i个位置开始的记录中的最小记录的索引 
             for(int j = i;j<a.length;j++){
                 if(a[j]<a[min]){
                     min = j;
                     continue;
                 }
             }

             //如果找到的最小索引与i不相等交换位置
             if(min!=i){
                 tmp = a[min];
                 a[min] = a[i];
                 a[i] = tmp;
             }

          }
         System.out.println(Arrays.toString(a));

     }

简单排序算法的改进–>二元选择排序

简单的选择排序,每趟循环只能确定一条记录排序后的位置。我们可以考虑改进成每趟循环确定俩条记录(当前趟的最大和最小记录)的位置,来达到减少循环次数的目的。改进后对n条数据进行排序,最多只需要进行n/2次循环即可。
 代码实现如下
 @Test
     public void test(){
         //定义待排序的数组 4,1, 9,2,7,5,3,6
         int a[] ={4,1,9,2,7,5,3,6};
         //定义中间变量   用于俩个数字交换时的转换
         int tmp;
         for(int i = 0; i<a.length/2; i++){
             int min = i;  //  默认起始位置为最小值的索引
             int max = i;  //默认起始位置为最大值的索引

             //找出从第i个位置开始的记录中的最小记录的索引和最大记录的索引 
             for(int j = i;j<a.length-i;j++){   //  注意这个地方要j<a.length-i, 因为当前趟从起始端和末尾的调整好了数据的位置,下一趟循环把不需要的减掉
                 if(a[j]<a[min]){
                     min = j;
                 }
                 if(a[j]>a[max]){
                     max = j;
                 }
             }
             //如果找到的最小索引与i不相等交换位置
             if(min!=i){
                 tmp = a[min];
                 a[min] = a[i];
                 a[i] = tmp;
             }
            //如果找到的最大索引与i不相等交换位置
             if(max!=i){
                 tmp = a[max];
                 a[max] = a[a.length-1-i];
                 a[a.length-1-i] = tmp;
            }
         System.out.println(Arrays.toString(a));
     }

4、选择排序–>堆排序

5、交换排序–>冒泡排序

 基本思想:在待排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的俩个数依次进行比较和调整,让较大的数往下沉,较小的数往上冒。即 当相邻的俩个数比较好发现它们的排序与排序要求相反时,就将它们互换。

上述思想每趟循环可能会导致许多数的位置发生变化,因为相邻的俩个数比较好发现它们的排序与排序要求相反时,就将它们互换,与简单选择排序不同,简单选择排序每趟循环最多俩个位置的数互换-->即俩个位置发生变化。 

冒泡排序的示例:对 49 38 65 97 76 13 27 49进行冒泡演示。

初始顺序:49 38 65 97 76 13 27 49 按照从小到大冒泡

第一趟排序: 38 49 65 76 13 27 49 97
第一趟排序原理: 第一条记录49与第二条记录38比较 根据排序要求 49 下沉 38 上冒 数组变成(38 49 65 97 76 13 27 49),接着用第二条记录49与第三条记录65比较,根据排序要求 数组保持原样(38 49 65 97 76 13 27 49),以此类推第。直到最后一条记录。 第一趟排序的结果是(38 49 65 76 13 27 49 97)

第二趟排序:38 49 65 13 27 49 76 97
第三趟排序:38 49 13 27 49 65 76 97
第四趟排序:38 13 27 49 49 65 76 97

由冒泡演示的示例得:冒泡每趟循环存在多个位置上的数据的变化。

基于上述的思想的一种代码实现。

 @Test
     public void test1(){
         //定义待排序的数组 49,38,65,97,76,13,27,49
         int a[] ={49,38,65,97,76,13,27,49};
         //定义中间变量   用于俩个数字交换时的转换
         int tmp;
         for(int i = 0; i<a.length; i++){
             for(int j = 0;j<a.length-i-1;j++){   //j<a.length-i-1中-i可以省略,省略之后只是多比较了几次,但是-1不能省略,如果不-1,即当j=a.length,for循环中j++会越界,因此j只能-1.使j不能取到最后一条记录,此时j++能取到最后一条。
                 if(a[j]>a[j+1]){    //前一个元素>后一个  则大的下沉   小的上冒。
                     tmp = a[j];
                     a[j] = a[j+1];
                     a[j+1] = tmp;
                 }
             }
          }
         System.out.println(Arrays.toString(a));
     }

冒泡排序算法的改进

 传统的冒泡排序中每一趟排序只能找到一个最大值或最小值,我们考虑利用在每一趟排序中进行正向和反向俩遍冒泡的方法一次可以得到俩个最终值(最大值和最小值),从而使排序趟数减少了一半。

改进后的算法的代码实现。
@Test
     public void testMaoPao(){
        //定义待排序的数组 49,38,65,97,76,13,27,49
         int a[] ={49,38,65,97,76,13,27,49};
        //定义中间变量   用于俩个数字交换时的转换
         int tmp;
         for(int i = 0; i<a.length/2; i++){
             //正向循环 把最大这下沉到 j+1的位置
             for(int j = i;j<a.length-i-1; j++){
                 //前一个数大于后一个   下沉
                 if(a[j]>a[j+1]){
                     tmp = a[j];
                     a[j] = a[j+1];
                     a[j+1] = tmp;
                 }
             }
             //反向循环 把最小者上冒的i的位置。
             for(int j = a.length;j>i;j--){
                 // 后一个数比前一个数小   上冒 
                 if(a[j]<a[j-1]){
                     tmp = a[j];
                     a[j] = a[j-1];
                     a[j-1] = tmp;
                 }
             }
         }
         System.out.println(Arrays.toString(a));

     }

交换排序—>快速排序

      基本思想:
      (1):选择一个基准元素,通常选择第一个元素或者最后一个元素。
      (2):通过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小,另一部门记录的值比基准值大。
      (3):此时基准元素在其排好序后的位置的正确位置。
      (4):然后分别对这俩部分记录用同样的方法继续进行排序,整个排序过程可以递归进行,直到整个序列有序。

        快速排序的示例:
       
        假设用户要排序的数组为   49  38  65  97  76  13  27  49 。
       
        基准数:一般我为第一个数   即 49  记为key=49。
       
        算法实现的代码实现思想:从数组的俩端交替的向中间扫描。
       
        方法其实很简单:分别从初始序列“49  38  65  97  76  13  27  49
”两端开始“探测”。先从右往左找一个小于key(49)的数,交换找到的数与key的位置,再从左往右找一个大于6的数,交换找到的数与key(本次key与上行的key的位置是不同的,因为上行的key已经做了交换)的位置。这里可以用两个变量i和j,分别指向序列最左边和最右边。我们为这两个变量起个好听的名字“哨兵i”和“哨兵j”。刚开始的时候让哨兵i指向序列的最左边(即i=0),指向数字49。让哨兵j指向序列的最右边(即=49),指向数字49。
这里写图片描述

      首先哨兵j开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵j先出动,这一点非常重要(请自己想一想为什么)。哨兵j一步一步地向左挪动(即j–),直到找到一个小于49的数(27),然后交换找到的数与基准数的位置,此时数组变成(27  38  65  97  76  13  49  49)。接下来哨兵i再一步一步向右挪动(即i++),直到找到一个数大于49的数(65),然后交换找到的数与基准算的位置。此时数组变为(27  38  49  97  76  13  65  49),左右两边交替循环直到i和j相等为止。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值