• 《啊哈算法》——排序


      今天开始了对《啊哈算法》这本书的学习。概括来讲,这本书算是算法界的小白书,语言很通俗,介绍的算法也比较简单,现在回来看简单的东西会不会显得浪费时间呢?不然,笔者最近感觉竞赛并不是终极之道,学习的方向也改为以书为对象(《具体数学》一栏的设立便可以看出),开始慢慢完善专业知识的体系,并慢慢做一些实用性的东西。

      对于排序问题,是算法需要解决的基本问题,而排序也有很多种,其不同的方法也决定了其不同的效率,下面我们来看几种常见的排序方法。

      桶排序:

      给出一组样例:将5、8、3、1、7从小到大的排序。

      桶排序基于一维数组,给出这样排序方法:初始化数组a[10] = 0,然后a[5]++、a[8]++、a[3]++、a[1]++、a[7]++。随后我们控制循环语句,访问a[i],并将i输出a[i]次,最终我们便会得到由小到大的排列。而这个过程,也像其名字所描述的,将数据i扔到了下标为i的数组元素a[i]这个桶里。

      桶排序巧妙的把数i当做一维数组的下标,而用a[i]记录i出现的次数,利用下标自小到大的排列顺序完成了对数据的排列。其优点是简易明了,但是缺点也很明显,我们所开的数组长度是受内存空间限制的,因此随着数据的扩大,该排序便显得捉襟见肘了。

      下面我们来简单的实现一下这个桶排序。

    #include<cstdio>
    int main()
    {
          int a[11] , i , j , t;
          for(i = 0;i <= 10;i++)
              a[i] = 0;
    
          for(i = 1;i <= 5;i++)
          {
               scanf("%d",&t);
               a[t]++;
          }
    
              for(i = 0;i <= 10;i++)
                  for(j = 1;j <= a[i];j++)
                    printf("%d ",i);
    
             
    
              return 0;
    }

      冒泡排序:

      对于一个需要由小到大排列的序列a[],我们通过比较相邻的两位a[i]、a[i+1],如果a[i+1] < a[i],则将a[i]与a[i+1]交换位置,此时排在序列末尾中又较小的数字就像气泡一样一点点向上冒,但问题在于每次这种交换较小的数字只向上冒了一格,而如果我们遍历了区间上所有相邻的情况,则在这一次遍历中,这个最小的数字将会升到序列的最前面(或者最大的数字沉到序列的最后面,这取决于从序列的头部还是尾部开始)。那么很显然,如果进行n - 1次遍历,我们便可以完成对n个数字的排序。

      由上面的算法分析我们不难看出,冒泡排序的时间复杂度是O(n^2),它虽然避免了桶排序浪费内存的弊端,但是其算法效率显得不是很客观。

      简单的代码实现如下。

    #include<cstdio>
    using namespace std;
    
    int main()
    {
          int a[100] , i , j , t , n;
          scanf("%d",&n);
    
          for(i = 1;i <= n;i++)
              scanf("%d",&a[i]);
    
        for(i  = 1;i <= n-1;i++)
              for(j = 1; j <= n - 1;j++)
             {
                   if(a[j] < a[j+1])
                   {
                     t = a[j];
                     a[j] = a[j+1];
                     a[j+1] = t;
                   }
             }
    
           for(i = 1;i <= n;i++)
               printf("%d ",a[i]);
    
           return 0;
    }

      快速排序:

      首先我们限定,排序方式是从小到大。

      快速排序基于一个精妙的二分思想,使得其排序效率非常快。假设给定一个序列a[1]到a[n],我们从下标为[1,n]开始,以a[1]为一个参考量,分别从左边和右边扫下标为[1,n]这个区间段,我们设置i记录从左边开始扫的下标,j表示从右边开始扫的下标,一旦发现a[i] > a[1]并且a[j] < a[1],则交换a[i]和a[j],直到两边的指针汇集到了一处,即i = j , 此时将a[i]与a[1]交换,则此时的序列顺序满足第i个元素的左边都是小于a[i]的,第i个元素的右边都是大于a[j]的。但是a[i]左边区间和右边区间的顺序仍然不符合要求,因此我们利用相同的方法,对左区间和右区间进行排序。显然,这不断地将区间一分为二然后分别排序,这呈现出一种类似于dfs回溯的方式,能够遍历到整个大区间的小区间,即完成对所有小区间的排序,也就完成了对整个区间上数字的排序。

      基于对快排的简单介绍,我们便可以进行编程实现了。

    #include<cstdio>
    int a[101] , n;
    
    void quicksort(int left , int right)
    {
         int i , j , t , temp;
         if(left >  right)
              return;
    
         temp = a[left];
         i = left;
         j = right;
         while(i != j)
         {
                while(a[j] >= temp && i < j)
                       j--;
                while(a[i] <= temp && i < j)
                      i++;
    
                  if(i < j)
                  {
                        t = a[i];
                        a[i] = a[j];
                        a[j] = t;
                  }
         }
    
         a[left] = a[i];
         a[i] = temp;
    
         quicksort(left , i - 1);
         quicksort(i + 1, right);
    }
    
    int main()
    {
          int i , j , t;
          scanf("%d",&n);
          for(i = 1;i <= n;i++)
                  scanf("%d",&a[i]);
    
          quicksort(1 , n);
    
          for(i = 1;i <= n;i++)
              printf("%d ",a[i]);
    
          return 0;
    }
  • 相关阅读:
    面试题6:用两个栈实现队列
    cygrunsrv: Error starting a service: QueryServiceStatus: Win32 error 1062: 解决办法
    支付系统的对账处理与设计--转
    centos 6.7下安装rabbitmq 3.6.6过程
    Can't access RabbitMQ web management interface after fresh install
    Spring Cloud Netflix Eureka client源码分析
    spring cloud config配置中心源码分析之注解@EnableConfigServer
    量化派基于Hadoop、Spark、Storm的大数据风控架构--转
    Inversion of Control Containers and the Dependency Injection pattern--Martin Fowler
    spark groupByKey 也是可以filter的
  • 原文地址:https://www.cnblogs.com/rhythmic/p/5440732.html
Copyright © 2020-2023  润新知