爱恨交织的算法恨只因爱的太深--算法思想篇

Posted Answer.AI.L

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了爱恨交织的算法恨只因爱的太深--算法思想篇相关的知识,希望对你有一定的参考价值。

壹、前  言  

  自从意识到算法的重要性之后,LZ对算法可谓是又爱又恨,爱是因为对算法学起来真的很有意思,

如果你以前有对数学这门学科情有独钟过的话,在接触了算法之后,你会自然而然地对算法产生爱慕之情。。。

之所以恨,是恨自己能力有限,算法太难,因为太爱所以会恨。。。

  学生时代,学校开数据结构这门课的时候,至今悔恨当初把课堂时间都给了睡眠。。。直到校招面试时,很多企业

都把算法提到台面时,回回都怒吃闭门羹。。。被问的哑口无言。。。LZ才下意识明白到了时间都去哪儿了??我啥就

这么差劲呢。。。大学的四年时光就这么毫无意义地流逝。。。

  算法对于逻辑思维能力的要求比较苛刻。。。一个问题的实现算法可以有很多种,,而我们需要做的就是写出一个

令人赏心悦目的算法实现,不管从代码的结构上,还是运行性能上,,我们都要尽可能地做到最好。。。

  解决同一个问题,,有的人可能扬扬洒洒写上上百行的代码,,而有的人却是言简意赅的十来行就能达到同样的目的。。。

  算法之所以这么吸引着我,,就是因为他解决问题的多样性。。。

 

贰、排 序 的 概 念

    在计算机程序开发过程中,对一组数据元素或记录按照某个关键字进行排序,排序完成的序列可用于快速查找相关记录的过程。

  由概念可以知道排序的目的是用于快速查找。

 

叁、如 何 衡 量 排 序 算 法 的 优 劣

  可以根据三个指标来衡量一个排序算法的优劣性:

    1):时间复杂度:分析关键字的比较次数和记录的移动次数;

    2):空间复杂度:分析排序算法中需要多少的辅助内存;

    3):稳定性:若两个关键字A和B的关键字值相等,但排序后A、B的先后次序保持不变,

          则称这种排序算法是稳定的;否则就是不稳定的。  

 

肆、排 序 的 分 类

  1、内部排序:整个排序过程中不需要借助于外部存储器(如磁盘等),所有排序操作都在内存中完成。

  2、外部排序:若参与排序的数据元素非常多,数据库非常大,计算机无法把整个排序过程放在内存中完成,

         必须借助于外部存储器(如磁盘等)。

   

  大分类下的子分类:

    1、内部排序算法分类:

      1):选择排序(直接选择排序堆排序)

      2):交换排序(冒泡排序快速排序)

      3):插入排序(直接插入排序折半插入排序Shell排序)

      4):归并排序

      5):桶式排序

      6):基数排序

    2、外部排序算法分类:

      1):多路归并排序

          将原文件分解成多个能够一次性装入内存的部分,分别把每一部分调入内存完成排序,

          接下来再对多个有序的子文件进行归并排序。

          即:将大文件分批调入内存中来进行排序,排序之后还要将结果输出到外存储器,待

            原文件所有记录都处理完毕后,最后再将以上分组排序好的结果进行两组两组地

            归并排序。

          注:在内存容量允许的条件下,每组中包含的记录越多越好,这样可以减少合并的次数。

 

伍、内 部 排 序 算 法 思 想

  1、直接选择排序

    第1趟:程序将记录定位在第1个数据上,用第1个依次和后面的每个数据进行比较,如果第1个数据大于后面的某个数据,

        交换他们……依次类推,直到与最后一个数据比较完之后,这组数据中的最小数据被选出,它被排在第1位。

    第2趟:程序将记录定位在第2个数据上,用第2个数据依次和后面的所有数据进行比较,如果第2个数据大鱼后面的某个数据,

        交换他们……依次类推,直到与最后一个数据比较完之后,这组数据中的第2小数据被选出,它被排在第2位。

        (注意:此时即第2趟排序中第1个数据不参与比较)

    后面依次类推……

  

  2、堆排序(以大顶堆为例)

    第1趟:将索引0~n-1处的全部数据建大顶堆,就可以选出这组数据的最大值,然后将大顶堆的根节点与这组数据的最

        后一个节点交换,就使得这组数据的最大值排在最后。

    第2趟:将索引0~n-2处的全部数据建大顶堆,就可以选出这组数据(0~n-2个数据,不包含第1趟已经选出的最大值)

        的最大值(注意第1趟中的最大值已被选出并不参与本趟比较,此处的最大值指的是0~n-2个数据中的最大值),

        将第2趟中的大顶堆的根节点与这组数据(0~n-1个数据,包含第1趟已经选出的最大值)的倒数第2个节点交换,

        就使得这组数据的第2大值排在倒数第2位。    

    后面依次类推……

    概念解释说明:

      大顶堆:将一组数据元素按照他们原有顺序排成一棵完全二叉树,并且树中所有节点的值都大于等于其左右子节点的值。

      小顶堆:将一组数据元素按照他们原有顺序排成一棵完全二叉树,并且树中所有节点的值都小于等于其左右子节点的值。

 

  3、冒泡排序

     第1趟:依次比较第0和1、1和2、2和3……n-2和n-1索引的元素,即相邻两个数据进行比较,如果前一个数据大于后一个

        数据,交换他们。经过第1趟排序之后,最大的元素就排在了最后。(第1趟有n个数据进行了比较)

     第2趟:依次比较第0和1、1和2、2和3……n-3和n-2索引的元素,如果前一个数据大于后一个数据,交换他们。

        (注意:第2趟比较的数据中不包含第1趟中已经选出的最大数据,即只有n-1个数据进行比较)。

        经过第2趟排序之后,第2大的元素排在了倒数第2位。

    后面依次类推……

       

  4、快速排序

    第1趟:从排序序列中任取一个值作为分界值(一般取第1个数据)。

        定义一个指针i,指向左边第一个元素(即索引下标0。若分界值取第1个数据,那指针i的索引所对应的元素值为分界值),

        用于找出序列中比分界值小的元素;

        再定义一个指针j,指向右边第一个元素(即索引下标为:n-1),用于找出序列中比分界值大的元素;

        拿指针j指向的索引所对应的元素和分界值比较,

          (1):如果小于分界值,则把指针j指向的元素赋给此时指针i所指向的索引;把目光切换向指针i,

            然后向后移动指针i(此时指针i指向的索引下标为1),把指针i指向的索引元素和分界值进行比较,

            [1]:如果小于分界值,则继续向后移动指针i,直到遇到指针i所指向的元素大于分界值;

            [2]:如果大于分界值,则把指针i指向的索引元素赋给此时指针j所指向的索引;切换为指针j移动,

          (2):如果大于分界值,则向前移动指针j,此处指针j指向索引下标为(n-2),

            依次类推,直到指针j所指向的元素小于分界值。

    后面的依次类推(递归)。。。

      

      注意点:指针i只负责找比分界值小的元素,指针j只负责找比分界值大的元素;

          一旦指针i指向的元素比分界值大,则把该元素赋给此时指针j所指向的索引,

            此时指针i不动,对指针j进行操作,向前移动指针j;

          一旦指针j指向的元素比分界值小,则把该元素赋给此时指针i所指向的索引,

            此时指针j不动,对指针i进行操作,向后移动指针i;

  

  5、直接插入排序

    对于有n个元素的一组数据来说,直接插入排序要进行n-1趟插入操作。具体步骤如下:     

    第1趟:将第2个数据与前面的有序序列进行一一对比,此时前面的有序序列只有1个数据。如果

      依次类推……

    第n-1趟:将第n个数据与前面的有序序列进行一一对比,先与第n-1个数据比较,如果第n个数据比第n-1个数据小,

         则将第n-1个数据向后移1位,再把第n个数据的值插入到序号为n-1的位置上,否则不对第n-1个数据进行移动;

         再与第n-2个数据进行比较,若第n个数据比第n-2个数据小,则将第n-2~n-1个数据全部向后移1位,

         再把第n个数据的值插入到序号为n-2的位置上,否则不对第n-2~n-1的数据进行移动;

         依次类推……

         最后与第1个数据数据进行比较,若第n个数据比第1个数据小,则将1~n-1个数据全部向后移1位,

         再把第n个数据的值插入到序号为1的位置上,否则不对第1~n-1个数据进行移动。

    注意:在每趟比较之前,需把第n个数据存储在一个变量中,防止整体移位时导致第n个数据丢失。

 

  6、折半插入排序

    折半排序是对直接插入排序进行了优化。

      直接插入排序中:当第n-1趟需要将第n个元素插入到前面的1至n-1个元素的序列中时,他总是会从第n-1个元素开始,

    逐一地比较每一个元素(比较顺序为:n-1,n-2,……,1),直到找到他的位置为止。。。这个显然没有利用到前面1至n-1个

    元素已经是有序序列这个特点。。。而折半插入排序就是对于这个进行了简单的优化。。。折半插入的具体操作如下:

       操作1:计算1至n-1元素的中间点(假设为他们中间点的元素为m),然后就拿第n个元素和m进行比较,如果第n个元素大,

          就直接在m~n-1的半个范围内搜索;反之,就在1~m的半个范围内搜索。。。这就是折半的大致思想。。。

       操作2:在半个范围内搜索时,再按照操作1中的步骤再次进行折半搜索。。。直到确定了第n个元素的插入位置为止。。。

 

  7、Shell(希尔)排序

    Shell排序是对直接插入排序进行了简单改进:它通过加大插入排序中元素之间的间隔,并在这些有间隔的元素中进行插入排序,

  从而使数据项大跨度地移动。当这些数据项排过一趟序后,Shell排序算法减少数据项的间隔再进行排序,依次进行下去。

  进行这些排序时的数据项之间的间隔被称为增量。习惯上用字母h来表示增量。

    举例说明:

      待排序列:-30  -16  21*  23  9  -49  21  30*  30

      第一趟排序:h = 9 / 2 = 4  (其中9为带排序列的元素个数)

          排序后:-30  -49  21*  23  9  -16  21  30*  30

      第二趟排序:h = 4 / 2 = 2

            排序后:-30  -49  9  -16  21*  23  21  30*  30

      第三趟排序:h = 2 / 2 = 1

                       排序后:-49  -30  -16  9  21*  21  23  30*  30

 

  8、归并排序

    归并排序是将长度为n的无序序列看成是n个长度为1的有序子序列,首先做两两合并,得到n/2个长度为2的有序子序列,

  再做两两合并……不断重复这个过程,最终得到一个长度为n的有序序列。。。

      归并排序算法的关键是在于"合并",合并的具体操作如下(假设:A和B为待合并的两有序序列):

      操作1:定义变量i,i初始化值为0;

      操作2:定义变量j,j初始化值为0;

      操作3:拿A序列中的i索引处元素和B序列中的j索引处元素进行比较,将较小的那个元素复制到一个临时数组中:;

      操作4:如果i索引处元素小,i++;如果j索引处元素小,j++;

      操作5:不断地重复上面4个步骤,即可将A、B两个有序序列中的数据元素复制到临时数组中去。知道A、B中任意的

          一个序列中的所有元素都被复制到临时数组中。最后,再将另一个有序序列中剩余的元素全部复制到临时数组中,

          合并完成,再将临时数组中的数据复制回去即可。。。

 

  9、桶式排序

    桶式排序不再是一种基于比较的排序方法,这种排序方法需要待排序列满足如下两个特征:

      1):待排序列的所有值处于一个可枚举的范围内;

      2):待排序列所在的这个可枚举范围不应该太大,否则排序开销太大。

    待补充。。。

 

  10、基数排序

     基数排序不是一种常规的排序方法,它更多地像是一种排序方法的应用,基数排序必须依赖于另外的排序方法。基数排序总体思路是

  将待排序的数据拆分成多个关键字进行排序,也就是说,基数排序的实质是多关键字排序。

    举例说明(最低位优先法):

    待排序列:192   221   13   23   

      第1轮比较个位:221   192   13   23   

      第2轮比较十位:13   23   221   192   

      第3轮比较百位:13   23   192   221

    附加说明:对于在基数排序时选择的关键字排序,选择哪种排序方式更加合适呢?答案是--桶式排序

    依据桶式排序的两个特征,且对于多关键字拆分出来的子关键字,他们一定位于0-9这个可枚举的范围内,

  这个范围也不大,因此用桶式排序效率非常好。

     

陆、结 束 语

   算法是一个很奇妙的东西,越是不会就越喜欢,因为它足够迷人,足够吸引着程序员的你去一探究竟。。。

虽然卤煮在这条道路上还是个菜鸟,如果不是一名算法工程师,平时也不会接触到太多算法知识,但是好奇心强的卤煮就是喜欢算法。

希望能和大家一起交流交流算法心得,或是各种专业知交流,毕竟技多不压身。。。好啦,今天就到这里了。。。

  父亲节,各位博友别忘了给父亲打个电话,问个好。那是我们一生的榜样,无可替代。。。父亲节快乐!!!

 

 如果你觉得博文写的不错,就点下【推荐一下】或【打赏】卤煮一杯奶茶吧!!!

以上是关于爱恨交织的算法恨只因爱的太深--算法思想篇的主要内容,如果未能解决你的问题,请参考以下文章

各种排序算法总结篇(高速/堆/希尔/归并)

爱恨交织的红黑树

趣味编程-数学篇-选择排序

经典算法动画解析系列:选择排序

OpenFeign和Consul爱恨交织的两天

程序设计基础之算法篇