算法基础
- 一、算法的基本概念
- 1.算法的特性
- ①有穷性
- ②确定性
- ③可行性
- ④有输入
- ⑤有输出
- 2.算法的优劣
- ①正确性
- ②可读性
- ③健壮性
- ④时间复杂度[^4]与空间复杂度[^5]
- 二、算法的描述
- 1.自然语言
- 2.流程图
- 1)流程图符号
- 2)3种基本结构
- ①顺序结构
- ②选择结构
- ③循环结构
- 3.N-S流程图
- ①顺序结构
- ②选择结构
- ③循环结构
一、算法的基本概念
算法(Algorithm) 是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。1
算法与程序设计及数据结构密切相关,是解决一个问题的完整的步骤描述,是解决问题的策略、规划和方法。正如著名计算机科学家Nkiklaus Wirth 2
数据结构+算法=程序
(Algorithm+Data Structures=Programs)
如果把程序比作一个人,那么数据结构就是人的躯体,而算法则是人的灵魂。
1.算法的特性
①有穷性
一个算法必须在执行有穷步之后结束,并且每一步都在有穷时间内完成,不能无限地执行下去。
有起点、有终点,不能无限延长。
起点
终点
例如一个计算1+2+…+99结果的程序:
#include<stdio.h>
int main()
{
int a,b=0;
a=1;//定义累加的起点。
while(a!=100)//设置累加的终点。
{
b=b+a;
a=a+1;
}
printf("1+2+...+99=%d",b);//输出累加结果b。
return 0;
}
此时可以从控制台上得出正确结果:
若没有设置累加的终点,那么程序将无休止地运行下去,也就是常说的死循环3,此时我们得不到我们想得到的结果。然而,在编程中死循环并不是一个需要避免的问题,相反,在实际应用中,经常需要用到死循环。例如,我们使用的Windows操作系统下的窗口程序中的窗口都是通过一个叫消息循环的死循环实现的。在单片机、嵌入式编程中也经常要用到死循环。在各类编程语言中,死循环都有多种实现的方法,以C语言为例,可分别使用while,for,goto实现。
②确定性
算法的每个步骤都应当是确切定义的,对于每个过程都不能有二义性,将要执行的每个动作都必须作出严格而清晰的规定。
例如在C语言中,为了避免出现嵌套的条件分支语句if-else的二义性,规定else总是与其前面最近且尚未配对的在同一复合语句中的if配对。
#include<stdio.h>
int main()
{
int a=1;
if(a==0)
printf("a=0。");
else//只与第5行的if配对。
if(a>0)
printf("a大于0。");
else//只与第8行的if配对。
printf("a小于零。");
return 0;
}
③可行性
算法中的每一步都应当有效地运行,也就是说算法是可执行的,并要求最终得到正确的结果。
例如,以下代码中的“c=a/b;”就是一条无效的语句,因为0是不可以做分母的。
#include<stdio.h>
int main()
{
int a=1,b=0,c;
c=a/b;//分母不能为0;
printf("c=%d",c);
return 0;
}
程序返回了一个非零值,说明程序遇到了问题而失败退出。
④有输入
一个算法可以有一个或多个输入,也可以没有输入。输入就是在执行算法时有必要从外界获取的,如算法所需的初始量等一些消息。
例如:
#include<stdio.h>
int main()
{
int a,b,c;
scanf("%d,%d,%d",&a,&b,&c);//有多个输入。
/*
其他程序代码。
*/
return 0;
}
又如:
#include<stdio.h>
int main()
{
//没有输入。
printf("Hello World!");
return 0;
}
⑤有输出
一个算法可以有一个或多个输出。输出就是算法最终所求的结果。编写程序的目的就是要得到一个结果。
例如,在控制台上输出“Hello World!”等。
如果一个程序运行下来没有任何结果,那么这个程序也就失去了意义。
2.算法的优劣
衡量一个算法的优劣性,通常从以下四个方面进行分析。
①正确性
正确性就是所写的算法能满足具体问题的要求。即对任何合法的输入,算法都会得出正确的结果。
②可读性
可读性是指算法被写好之后,该算法被理解的难易程度。一个算法可读性的好坏十分重要,因为如果一个算法比较抽象,难以理解,那么这个算法就不易交流和推广使用,也不方便修改、扩展和维护。
③健壮性
一个程序编写完成后,运行程序的用户对程序的理解各有不同,并不能保证每个人都能按照要求进行输入。健壮性就是指当输入的数据非法时,算法也会作出相应判断,而不会因为输入的错误造成瘫痪。
④时间复杂度4与空间复杂度5
简单的说,时间复杂度就是算法运行所需要的时间。不同的算法具有不同的时间复杂度。当一个程序特别大时,时间复杂度是十分重要的。因此,写出更高速的算法一直是算法不断改进的目标。空间复杂度就是指算法运行所需的存储空间的多少。
二、算法的描述
对于一些问题的求解步骤,需要一种表达方式,即算法描述。
1.自然语言
自然语言是指人们在日常生活中说使用的语言,这种表达方式通俗易懂。
例如如何将衣物放入箱内?描述如下:
1.打开箱子。
2.将衣物整理好放入箱内。
3.关闭箱子。
采用自然语言描述的好处是易懂,但是也有很大的弊端,就是容易产生歧义。因此,在一般情况下不采用自然语言进行描述。
2.流程图
流程图是算法的图形化表示法,它用一些图框来代表各种不同性质的操作,用流程线来指示算法的执行方向。由于流程图直观形象、易于理解,所以应用比较广泛。
1)流程图符号
流程图是使用一些图框来表示各种操作的。其中,起止框用来标识算法的开始和结束;判断框的作用是对一个给定的条件进行判断,根据给定的条件是否成立来决定如何执行后续操作;连接点用来将画在不同地方的流程线连接起来。
例如如何将衣物放入箱内的实现过程用流程图表达的效果如下图所示:
Created with Raphaël 2.2.0 开始 打开箱子 将衣物整理好放入箱内 关闭箱子 结束
2)3种基本结构
1966年,计算机科学家Bohm和Jacopini为了提高算法的质量,经过研究,提出了3种基本结构,即顺序结构、选择结构和循环结构,因为任何一个算法都可以由这3种基本结构组成, 结构化算法能解决任何复杂的问题。 这3种结构之间可以并列、相互包容,但不允许交叉,即不允许从一个结构直接转到另一个结构的内部。
①顺序结构
顺序结构是简单的线性结构。
在执行完A所指定的操作后,接着执行B所指定的操作,在这个结构中只有一个入口点A和一个出口点B。
②选择结构
选择结构也称为分支结构,在选择结构中必须包含一个判断框。
如图所示的选择结构是判断给定的条件P是否成立,如果条件成立则执行A语句,否则什么也不做。
③循环结构
在循环结构中,反复地执行一系列操作,直到条件不成立才终止循环。按照判断条件出现的位置,可将循环结构分为当型循环和直到型循环。
当型循环结构如下图所示:
此当型循环先判断条件P是否成立,如果成立,则执行A语句;在执行完A语句后,再判断条件P是否成立,如果成立,接着再执行A语句;如此反复,直到条件P不成立为止,此时不执行A语句,跳出循环。
直到型循环结构如下图所示:
此直到型循环先执行A语句,然后判断条件P是否成立,如果成立则再执行A语句;然后判断条件P是否成立,如果成立,接着再执行A语句;如此反复,直到条件P不成立,此时不执行A语句,跳出循环。
3.N-S流程图
N-S流程图是另一种算法表示法,是1973年由美国学者Ike Nassi和Ben Shneiderman共同提出的,其依据是:既然任何算法都是由顺序结构、选择结构及循环结构这3种基本结构组成的,那么各基本结构之间的流程线就是多余的,因此去掉了所有的流程线,将全部算法写在一个矩形框内。N-S流程图也是算法的一种结构化描述方法,同样也有3种基本结构。
①顺序结构
顺序结构如下图所示:
②选择结构
选择结构的N-S的流程图如下图所示:
例如,判断输入的数字是否是偶数:
③循环结构
(1)当型循环结构的N-S流程图如图所示。
例如,求1+2+…+99的值:
(2)直到型循环结构的N-S流程图如下图所示。
例如,求1+2+…+99的值:
这3种基本结构都只有一个入口和一个出口,结构内的每一部分都有可能被执行,且不会出现无休止循环的情况。