什么是算法

  • 算法是解决特定问题的步骤的描述,在计算机中表现为指令的有限序列,并且每条指令表示一个或多个操作。
  • 算法有助于理解数据结构,且程序设计 = 数据结构 + 算法
  • 算法的特性:输入、输出、有穷性、确定性和可行性。
    • 输入、输出:
      • 算法具有零个或多个输入
      • 算法至少有一个或多个输出
    • 有穷性:指算法在执行有限的步骤后可以自动结束,而不会出现无限循环。并且每一个步骤都在可接受的时间内完成。
    • 确定性:算法的每一步都有具体、确定的含义,不会出现二义性。
    • 可行性:算法的每一步都能够通过执行有限的次数完成。可转换为程序上机运行,并得到正确的结果。
  • 算法的设计要求:正确性、可读性、健壮性和高效性。
    • 正确性:指算法应该至少具有输入、输出、无歧义的处理过程,能正确地反映需求,得到问题的正确答案。具体的:
      • 1.程序没有语法错误。
      • 2.对合法输入能产生满足要求的输出。
      • 3.对非法输入能产生对应说明的结果。
      • 4.对于极端测试数据能产生满足要求的输出。
      • 以上难度逐级递增,一般情况下将层次3作为判断一个算法是否正确的标准。
    • 可读性:便于阅读、理解和交流。
    • 健壮性:输入非法时,算法能做出相关处理,而不是产生异常或者直接崩掉。
    • 高效性:时间效率高、存储量低。
  • 算法效率的度量方法:
    • 事后统计法:针对设计好的测试程序或数据,通过计算机运行对比耗时,从而确定算法效率高低。有很大缺陷:
      • 1.必须依据算法事先写好程序。
      • 2.依赖硬件、软件等环境。
      • 3.测试数据设计困难。测试数据规模大小有可能影响算法的表现。
    • 事前分析估算方法:在编写程序前,依据统计方法对算法进行估算。
      • 明确:分析程序的运行时间时不关心程序由什么语言编写,不关心跑在什么硬件上,只关心实现它的算法。
      • 依据算法时间复杂度估算算法时间效率:
        • 影响程序运行消耗时间因素一般为:
          • 1.算法采用的策略及代码质量。
          • 2.编译后的代码质量。
          • 3.输入数据的规模。
          • 4.计算机执行速度。
          • 其中2、4分别为软件、硬件影响。抛开软硬件因素,一般关注算法的好坏和输入的规模。
        • 函数的渐近增长:
          • 定义:规定,在输入规模n没有限制的情况下,如果存在一个整数N,使得对于所有的n>N,函数f(n)总比函数g(n)大,则我们说f(n)的增长渐近快于g(n)。
          • 判断一个算法的效率时,函数中除最高阶项外,其他次要项常常可被忽略。由上图也可看出,判断算法的好坏,只通过少量数据是无法做出准确判断的。
        • 算法时间复杂度(算法的时间度量)
          • 定义:在进行算法分析时,语句的总执行次数T(n)是关于问题规模n的函数,通过分析T(n)随n的变化情况可确定T(n)的数量级,记作:T(n)=O(f(n))。它表示随着问题规模n的增大,算法执行时间的增长率和问题相关函数f(n)的增长率相同,称作算法的渐近时间复杂度,简称时间复杂度。该记法:O(f(n))称为大O记法。
          • 推导大O阶方法应遵循:
            • 1.用常数1取代运行时间为常数的结果。即若执行次数的大小与n值变化无关,执行时间恒定,我们称之为具有O(1)的时间复杂度,又叫常数阶。
            • 2.只保留最高阶项。
            • 3.最高阶项系数若不为1,按1处理。
          • 常见复杂度:
            • 常数阶 O(1):一般就是大于等于一条独立语句或单纯的分支结构。
            • 线性阶 O(n):一般为循环结构。
            • 对数阶 O(logn)、O(nlogn):一般为循环结构。
              • 如下图,count的取值就是个等比数列:2⁰ 2¹ 2² … 2^x = n;则x的结果即是该行代码执行的次数,就有:2^x = n 👉 x = log₂n,因此这段代码的时间复杂度为O(log₂n)。
            • 平方阶 O(n²):一般为嵌套循环。
            • O(m+n):由两个数据规模m、n组成,无法事先评估m和n谁的量级大。
              • 如下图,无法判断哪个for的循环次数多,因此时间复杂度为O(m+n)。
            • 常见的复杂度:
        • 最好、最坏情况时间复杂度
          • 最好情况时间复杂度就是,在最理想的情况下,执行这段代码的时间复杂度。
          • 最坏情况时间复杂度是一种保证,意味运行时间将不会再坏了。
            • 在应用中,除非特别指定,通常提到的运行时间都是最坏情况的运行时间。
          • 如下图,当数组第一个元素刚好是要查找的变量x,即是最好的时间复杂度:O(1);当数组中没有要查找的变量x,必须把整个数组遍历一遍,即是最坏的时间复杂度:O(n)
        • 平均时间复杂度
          • 平均运行时间是对程序的期望运行时间,但现实中很难通过分析得到,一般是通过运行一定数量的实验数据后估算出来的。
          • 平均的方法,有的认为就是简单的平均——假定每一种情况发生的可能性是相同的(大话数据结构);有的认为是加权平均(数据结构与算法之美),两者有可能计算得到的时间复杂度没有差别,但其中的过程还是有所不同。我个人认为加权平均更靠谱,不过把两个都掌握也不是难事。
          • 如下图,用两种算法得到的时间复杂度一样,具体结果有差:
            • 简单平均:x在数组的 0~n-1 位置中和不在数组中,假定每种情况的可能性相同,需要遍历的元素个数平均值为:1*(1/(n+1))+2*(1/(n+1))+3*(1/(n+1))+...+n*(1/(n+1))+n*(1/(n+1)) = n(n+3)/(2(n+1)),平均时间复杂度为O(n);
            • 加权平均:首先x要么在数组里要么不在,假设在数组中与不在数组中的概率都为 1/2,x在数组的 0~n-1 位置中的概率一样,为1/n。那么需要遍历的元素个数平均值为:1*(1/2n)+2*(1/2n)+3*(1/2n)+...+n*(1/2n)+n*(1/2) = (3n+1)/4,平均时间复杂度为O(n)。
        • 均摊时间复杂度与摊还分析
          • 如下图,实现往数组中插入数据的功能。数组没满则直接插入数据,数组满了就用循环遍历数组求和,将sum值放到数组的第一个位置,再将新的数据插入。
          • 假设数组的长度是为n,根据数据插入的位置的不同,可以分为 n 种情况,每种情况的时间复杂度是 O(1)。此外,还有一种“额外”的情况,即数组没有空闲空间时插入一个数据,循环遍历求和,这个时候的时间复杂度是 O(n)。而且,这 n+1 种情况发生的概率一样,都是 1/(n+1)。根据加权平均,则有:1*(1/(n+1))+1*(1/(n+1))+1*(1/(n+1))+...+1*(1/(n+1))+n*(1/(n+1)) = 2n/(n+1),平均时间复杂度为O(1)。
          • 但是这段插入代码与平均时间复杂度里的查找代码是有区别的:查找代码在极端情况下复杂度才是O(1),但插入代码绝大部分情况下时间复杂度都是O(1),只有极个别情况复杂度才为O(n);对插入代码来说,O(1)和O(n)的插入出现的频率很有规律,有一定的前后时序关系,因此针对这一特殊场景可以使用摊还分析法,将耗时多的那次操作均摊到耗时少的操作上,则这组代码的均摊时间复杂度即为O(1)。
          • 总结:对一个数据结构进行一组连续操作中,大部分情况下时间复杂度都很低,只有个别情况下时间复杂度比较高,而且这些操作之间存在前后连贯的时序关系,这个时候,我们就可以将这一组操作放在一块儿分析,看是否能将较高时间复杂度那次操作的耗时,平摊到其他那些时间复杂度比较低的操作上。而且,在能够应用均摊时间复杂度分析的场合,一般均摊时间复杂度就等于最好情况时间复杂度。
      • 依据空间复杂度评价算法:
        • 表示算法的存储空间与数据规模之间的增长关系。
        • 算法的空间复杂度通过计算算法所需的存储空间确定,公式记作: S(n)= O(f(n)) ,其中,n为问题的规模, f(n)为表现与n相关的存储空间占用情况的函数。
        • 如下图,函数内第一行申请了一个空间存储变量i(常量阶被忽略),第二行申请了一个大小为n的vector,除此之外剩下的代码没有占用更多的空间,因此争端代码的空间复杂度为O(n)。
  • 如何去学算法:
    • 15mins如果还是没头绪,就果断去看答案,看懂答案。
    • 看懂分三步:流程 👉 每个语句的功能 👉 试数。
    • 敲代码,不断出错不断优化,日积月累。
    • 重要的,不要觉得受打击,明白自己处于什么水平,会出现什么问题,怎么去解决,消耗多长时间...
    • 特殊的:某算法一直看得似懂非懂始终搞不定,就追求大致看懂,背会,在后续学习过程中可能就会醍醐灌顶。
  •  
posted @ 2023-01-12 22:21  苏显  阅读(862)  评论(0编辑  收藏  举报