数据结构与算法学习总结-算法分析基础

来源:互联网 发布:c语言漫画 编辑:程序博客网 时间:2024/05/21 09:52

  算法(algorithm)是为求解一个问题需要遵循的、被清楚指定的简单指令的集合。是明确定义的可计算过程,以一个数据集合作为输入,并产生一个数据集合作为输出。一个算法通常具有以下五个特性:
  输入:一个算法应以待解决的问题的信息作为输入;
  输出:输出对应指令集处理后得到的信息;
  可行性:算法是可行的,即算法中的每一条指令都是可以实现的,均能在有限的时间内完成;
  有穷性:算法执行的指令个数是有限的,每个指令又是在有限时间内完成的,因此整个算法也是在有限时间内可以结束的;
  确定的:算法对于特定的合法输入,其对应的输出是唯一的。即当算法从一个特定输入开始,多次执行通一指令集结果总是相同的。
  注:对于随机算法,算法的第五个特性应当被放宽。
  简单的说,算法就是计算机解题的过程。在这个过程中,无论是形成解题思路还是便携程序,都是在实施某种算法。前者是算法的逻辑形式,后者是算法的代码形式。
  对于一个问题,一旦某种算法给定并且被确定是正确的,那么重要的一步就是确定该算法将需要多少如时间、空间等资源量的问题。

1.时间复杂性

  以简单选择排序作为例子:
  令A[0,n-1]为n个数据元素的数组,将该数组排序为一个非降序的有序数组。
  k=0:首先在n个元素中找到最小元素,将其放在A[0]中;
  k=1:然后再剩下的n-1个元素中找到最小的放到A[1]中;
  k=1,2,3…循环此过程;
  k=n-1:直到结束,即排序完成。
对六个整数进行简单选择排序
  输入:整数型数组a[0,n-1]
  输出:按非降序排列的数组a[0,n-1]
  代码:

public void selectSort(int[] a){    int n = a.length;    for(int k = 0;k < n-1;k++){        int min = k;        for(int i = k+1;i < n;i++){            if(a[i] < a[min]){min = i;}        }        if(k != min){            int temp = a[k];            a[k] = a[min];            a[min] = temp;        }    }}

  可以得出该算法执行的比较次数为:
  这里写图片描述
  同时也可以看出数据元素交换的次数在0到n-1之间,而每次交换需要使用3条赋值语句,因此数据元素的赋值次数在0到3(n-1)之间。
  为了避免算法所运行时的硬件、操作系统以及使用的高级语言编译系统所造成的影响,我们设定一些基本操作都是可以再一个常数时间内完成。这样,算法执行基本操作的次数可以反映算法的运行时间。
  关于规模为n的非负函数,其运行时间记为T(n)。
  对于上述的算法则有:
  这里写图片描述(c为某个正整数 )
  定义1:如果存在正常数c和n0使当N≥n0时T(N)≤cf(N),记为:T(N)=O(f(N));
  定义2:如果存在正常数c和n0使当N≥n0时T(N)≥cg(N),记为:T(N)=Ω(g(N));
  **定义3:**T(N)=θ(h(N))当且仅当T(N)=O(h(N))和T(N)=Ω(h(N));
  定义4:如果T(N)=O(p(N))且T(N)≠Ω(p(N)),则T(N)=o(p(N))。
  上述定义中:
  O符号给出了算法时间复杂度的上界;
  Ω符号在运行时间的常数因子范围内给出了时间复杂度的下界;
  θ符号给出了算发时间复杂度的精确阶。

2.空间复杂性

  算法的空间复杂性同样是由算法运行时使用的空间来评价的。
  定义:为了求解问题的实例而执行的操作所需要的存储空间的数目,但是它不包括用来存储输入实例的空间。
  算法的空间复杂性是以时间复杂性为上界的。在算法中每访问一次存储空间都是需要使用一定时间的,即使每次访问的都是不同的存储空间,空间的大小也不会超过基本操作次数常数倍,因此算法的空间复杂性是以时间复杂性为上界的。
  使用S(n)和T(n)分别表示算法的空间复杂度和时间复杂度,则有S(n)=T(n)。

3.时间复杂度分析

  获取一个程序的运行时间,最简单的方法就是将算法执行的所有基本操作都计算出来,然后得出算法的时间复杂度。但是很多时候这种方法时不可取的,因为它太麻烦而且可能计算不出所有基本操作的执行次数。
  一般的,存在集中算法思想,而我们总愿意隐藻出去哪些不好的算法思想,因此,通常需要分析算法。不仅如此,进行分析的能力常常提供对于设计有效算法的洞察能力。一般来说,分析还能准确的确定瓶颈-这些地方值得仔细编码。
  为了简化分析,我们抛弃一些常数以及低阶项,只计算大O运行时间,即O(f(n))。实际上,分析大O,为程序在一定的时间范围内能够终止运行提供了保障,程序可能提前结束,但绝不会错后。
  一般法则:
  For循环:一个for循环的运行时间至多是该for循环内部哪些语句(包括测试)的运行时间乘以迭代次数。
  嵌套的for循环:从里向外分析这些循环。在一组嵌套循环内部的一条语句总的运行时间为该语句的运行时间乘以该组所有的for循环的大小的乘积。
  顺序语句:将各个语句的运行时间求和即可,即其中的最大值就是所得的时间复杂度。
  If/else语句:一个if/else语句的运行时间从不超过判断的运行时间再加上判断体和执行体重运行时间长者的总的运行时间。

附:
  联机算法:只对数据进行一次扫描,主存中不必存储传入数据的任何部分,在任意时刻都能获得关于该数据的某个问题的答案。具有这种特性的算法成为联机算法。仅需要常量空间并以现行时间运行的连击算法几乎是完美的算法。
  在此类算法中,通常会舍弃可以获取目标数据值(数集)的位置或可以获得其他无关紧要的信息的代码,只求过程的极简和结果的正确性。
  运行时间中的对数:如果一个算是用常数时间(O(1))将问题的大小削减为其一部分(通常是1/2),那么该算法就是O(logN)。如果使用常数时间只是把问题减少一个常数的数量(如将问题减少1),那么这种算法就是O(N)的,如折半查找、欧几里得算法、幂运算。

4.分析结果的准确性

  有时候,我们的时间复杂度分析会过大。这时,或许我们需要进一步细化分析,或需可能是平均运行时间显著小鱼最坏情形的运行时间,不可能对所得的界再加以改进,对于许多复杂的算法,最坏的界通过某个坏的输入是可以达到的,但再实践中它通常是过大的。遗憾的是,对于大多数这类的问题,平均情形的分析是及其复杂的(在许多情形下仍然悬而未决),而最坏情形的界尽管过分悲观,但却是最好的已知解析结果。

原创粉丝点击