第二章 算法
1.算法的定义:算法是解决特定问题求解步骤的描述,在计算机中表现为指令的有限序列并且每条指令表示一个或多个操作。
输入(零个或多个输入)
输出(一个或多个输出)
算法效率的度量方法
1.事后统计方法:这种方法主要是通过设计好的测试程序和数据,利用计算机计时器对不同的算法编制的程序的运行时间进行比较,从而确定算法效率的高低。(这种方法有很大缺陷,不给予采纳)
2.事前分析估算方法:在计算机程序编制前,依据统计方法对算法进行估算。
一个程序的运行时间,依赖于算法的好坏和问题的输入规模。(输入规模是指输入量的多少)
例子:两种求和的算法每行代码的执行次数
第一种算法:for循环
int i, sum = 0, n = 100; //执行了一次
for (i = 1; i <= n; i++) //执行了n+1次
{
sum = sum + i; //执行了n次
}
printf ("%d", sum); //执行了1次
第二种算法:
int sum = 0, n = 100; //执行了一次
sum = (1 + n) * n/2; //执行了一次
printf("%d", sum); //执行了一次
第一种算法执行了n次,第二种算法执行了1次。
我们再延伸一个例子
int i, j, x = 0,sum = 0,n = 100; //执行了一次
for (i = 1; i <= n; i++) //执行了n+1次
{
for (j = 1; j <= n; j++) //执行了nx(n+1)次
{
x++; //执行了nxn次
sum = sum + x;
}
}
printf ("%d", sum); //执行了n次
我们把循环看作一个整体,忽略头尾的执行,只取最中间的部分,所以上述代码整体需要执行n²次。
总结:测定时间最可靠的方法就是计算对运算时间有消耗的基本操作的执行次数。运行时间和这个计数成正比。
①常数阶:O(1)
下面这个算法的时间复杂度为O(1)
int sum = 0, n = 100; //执行了一次
sum = (1 + n) * n/2; //执行了一次
printf("%d", sum); //执行了一次
当算法中的语句sum = (1 + n) * n/2; 有10句时,即:
int sum = 0, n = 100; //执行了一次
sum = (1 + n) * n/2; //执行了1次
sum = (1 + n) * n/2; //执行了2次
sum = (1 + n) * n/2; //执行了3次
sum = (1 + n) * n/2; //执行了4次
sum = (1 + n) * n/2; //执行了5次
sum = (1 + n) * n/2; //执行了6次
sum = (1 + n) * n/2; //执行了7次
sum = (1 + n) * n/2; //执行了8次
sum = (1 + n) * n/2; //执行了9次
sum = (1 + n) * n/2; //执行了10次
printf("%d", sum); //执行了一次
实际上无论n为多少,上面两个代码就是3次和12次执行的差异。这种与n的大小无关,执行时间恒定的算法,我们称之为具有O(1)的时间复杂度,又叫常数阶。
/ 注:不管这个常数是多少,我们都记作O(1)/
②线性阶:O(n)
下面这段代码,它的循环的时间复杂度为O(
n)
int i, //执行了一次
for (i = 1; i <= n; i++) //执行了n+1次
{
//执行了n次
}
printf ("%d", sum); //执行了1次
③对数阶:O(logn)
下面的代码时间复杂度为O(log(2)x)
int x= 1, //执行了一次
while (x<n)
{
x = x* 2; //执行了2^x次
}
④平方阶:O(n²)
下面的代码时间复杂度为O(n²)
int i, j, x = 0,sum = 0,n = 100; //执行了一次
for (i = 1; i <= n; i++) //执行了n+1次
{
for (j = 1; j <= n; j++) //执行了nx(n+1)次
{
x++; //执行了nxn次
sum = sum + x;
}
}
printf ("%d", sum); //执行了n次
常用的时间复杂度所耗费的时间从小到大: