天天看点

[算法] - 入门

序    

  这一篇文章是算法专栏第一篇文章。算法这个栏目会连载常用的算法(代码为Java),欢迎大家给我留言讨论。

1.什么是算法

  1.1 算法定义

    通俗的讲,算法是对问题求解过程的一种描述,是为解决一类问题给出一个确定的,有限的操作序列。曾经获得图灵奖的著名计算机科学家 D.Knuth 对算法做过一个为学术界广泛接受的描述性定义: 一个算法(angorithm)就是一个有穷规则的集合,其规则确定了一个解决某一特定问题的操作序列,算法的规则必须满足5下特征:

    有穷性 --- 算法必须在执行有穷步骤后结束

    确定性 --- 算法的每一个步骤必须是确切定义的

    输入 ---    算法有零个或者多个输入

    输出 ---    算法有一个或者多个输出,与输入有某种特定关系

    可行性 --- 算法中有待执行的操作必须是相当基本的。即是说,他们原则上都是能精确的进行的,而且用笔和纸做有穷次就可以完成的

    有穷性  和 可行性 是算法最重要的两个特征

  1.2 算法的描述

    算法可以用文字、高级程序设计语言或类似于高级程序设计语言的伪代码描述,此时,算法是由语义明确的操作步骤组成的有限序列。它精确的指出怎样从给定的输入信息得到要求的输出信息。

    例如:在学生信息表中,按照姓名进行顺序查找(sequential search) 的算法思路为:对于给定值k 从线性表的一端开始,以此比较每个元素的姓名栏,如果存在姓名与k元素相同的数据,则查找成功,否则查找失败,这种方法是可行的,但是查找范围大,耗时长,查找不成功时,需要比较表中所有数据。

  1.3 算法与数据结构

    算法是建立在数据的逻辑结构和存储结构上的,数据结构和算法之间存在着本质的联系,失去一方,另一方就没有意义,在研究一个数据结构时,总是离不开对这个数据结构进行的各种操作。而且只有通过对这些操作的算法研究,才能更清楚的理解这中数据结构的意义和作用,反过来 在研究一种算法的时候,也总是自然地联系到这种算法建立在什么数据结构上。

  1.对于同样的逻辑结构和存储结果,根据业务不同,可能会采用不同的算法。

  2.同样的数据结构,不同的存储结构,则采用的算法必然不同。

  存储结构不同的线性表其排序算法必然不同,例如 冒泡排序, 折半插入排序等,适用于顺序存储的数据结构,而不能用于链式存储的线性表,适用于链式存储的线性表的算法有 直接插入算法,简单选择排序等。

  1.4算法与程序

    算法使用抽象的语言描述解决的问题的每一步的操作,程序是计算机能够理解执行的指令序列,其中指令既可以是机器指令,汇编指令,也可以是高级语言的语句指令,通常一个程序实现一个算法,算法和程序的区别是:算法的执行是有穷的,而程序的执行可以是无限的,例如操作系统 只要系统不破坏 操作系统可以一直执行

2.算法的设计

  2.1算法设计目标

    算法设计应该满足以下5个目标:

    正确性 --- 算法应该确切的满足具体问题的需求,这是算法设计的基本目标

    可读性 --- 算法的可读性有利于人对算法的理解,这既有利于程序的调试和维护,也有利于算法的交流和移植,算法可读性注意体现在两方面,一是类名方法名 要见名知意,二是要有足够的注释

    健壮性 --- 输入非法数据时,算法要能做出适当的处理,而不是产生不可预估的错误

    高时间效率--- 算法时间效率指算法的执行时间,执行时间短的算法称为高时间效率算法

    高空间效率--- 算法在执行时一般要求额外的内存空间,内存要求低的算法称为高空间效率算法

    对于同一个问题,如果有很多种算法,应该尽量选择 高时间效率和高空间效率算法,算法的高事件效率和高空间效率一般是矛盾的,优先选择高时间效率

  2.2 算法的分析 - 时间复杂度

    算法执行时间等于所有语句执行时间总和,是算法所处理的数据个数n的函数,表示为 O(f(n)) O(f(n))称为该算法的时间复杂度(time complexity)O(1) 表示时间是个常数,不依赖于n O(n)表示时间与n成正比,是线性关系。O(n^2) 、O(n^3) 和 O(n^n) 分别称为平方阶,立方阶,指数阶,O(log2n) 为对数阶。

    通常用算法时间复杂度来表示算法的时间效率,若2个算法执行时间分别是 O(1) 和 O(n) 当n充分大时,显然O(1) 执行的时间更少, 同样 O(n) 和 O(log2n) 相比较时,当N充分大时,因为log2n值比n小,则 O(log2n)所对应的算法要快的多。

    下面我用详细的例子个大家演示:

    首先是  O(1) 的算法,一个简单的语句算法复杂度就是 O(1),该语句执行时间是个常量。

sum=1      

    一个循环的时间复杂度是 O(n),for循环执行了n次 其中循环体内语句的执行了一次,时间是个常量 可以忽略不计,所以for循环时间复杂度是 O(n)

int n=0,sum=0;
for (int i=0;i<n;i++) {
  sum+n;
}      

    时间复杂度 O(n^2) 的二重循环, 外层循环执行了n次,每执行一次外层循环,内层循环就执行n此,所以二重循环的循环体语句执行了n*n次,

int n=0,sum=0;
for (int i=0;i<n;i++) {
  for(j=0;j<n;j++) {
       System.out.println("hello liangtian"+i*j);
    }
}      

  这种 外层循环执行了 n次  内层循环执行了 i次  也是 O(n^2)

int n=0,sum=0;
for (int i=0;i<n;i++) {
  for(j=0;j<i;j++) {
       System.out.println("hello liangtian"+i*j);
    }
}      

  时间复杂度为 O(nlog2n),外层循环执行了 log2n次外层循环没执行一次 i就乘以2,直到k>n 内层循环执行次数是n 

int n=0,sum=0;
for (int i=0;i<n;i*=2) {
  for(j=0;j<n;j++) {
       System.out.println("hello liangtian"+i*j);
    }
}      

  时间复杂度为 O(n) 的二重循环, 外层循环执行 log2n  内层循环执行 i次 随着外层循环的增长而成倍增长,

int n=0,sum=0;
for (int i=0;i<n;i*=2) {
  for(j=0;j<i;j++) {
       System.out.println("hello liangtian"+i*j);
    }
}      

时间复杂度表:

  

[算法] - 入门

  2.3 算法的分析 算法空间复杂度

      与算法的时间复杂度类似,算法在执行时要求的额外内存空间称为算法的空间复杂度。执行一个算法所需要的存储空间包括三部分:

  1. 输入数据占据的存储空间
  2. 程序之灵占用的存储空间
  3. 辅助编了占用的存储空间

  其中输入数据和程序之灵所占用的存储空间与算法无关,因此辅助变了占用的存储空间就称为度量算法空间代价的依据。

  当问题的规模以某种单位从1增长到n时候,解决这个问题的算法在执行时所占用的存储空间也以某种单位从1增加到 S(n) 则称此算法空间复杂度 (Space Complexity)为S(n). 当n增大时候,随着S(n)也随之增大,空间复杂度用 大O表示基座S(n)=O(f(n)) 表示该算法的空间增长率 与 f(n) 的增长率相同。

  例如,交换两个边路 i,j 算法,除了程序之灵和 i.,j 本身占用的存储空间,为了实现交换操作  还必须声明一个临时变量  temp ,这个 temp 变量所占用的1个存储单元就是交换变量算法的空间复杂度  O(1).