上课,同学们好!
今天开始,我将讲解算法大总结。那么让我们开始吧。首先,我今天要讲的是:递归算法。
我觉得讲课要讲得透彻,必须让学生知其所以然。所以,我第一要讲的是:为什么需要递归算法?
让我们看下面这个数学式子:f(n)=f(n-1)+f(n-2),f(0)=0,f(1)=1;
非常明显,若我们想求出f(N),如果不使用递归,那么就必须使用烦琐的嵌套for语句,更要命的是,当N→∞时,for语句将会变成程序的瓶颈,因为它需要大量时间计算!!最终很可能无法“得到”答案。
如果使用递归,那么,程序在时间上的开销将大大降低,当然,其内存消耗会比前面的嵌套for语句要多。
在时间、内存权衡情况下,我们往往宁愿多牺牲内存,毕竟,扩充内存总比等待很长(有时甚至无法“解出”答案)时间才得出答案要好!
那么,我们如何写递归算法呢?
拿上面所提到的数学式子来说,其递归算法是这么写出的:
long function(int N)
{
if(N==0) return 0;
if(N==1) return 1;
if(N>1)
return (function(N-1)+function(N-2));
return -1;
}
看上去很简单,不过却包含了写递归算法的准则:
1,必须要有基本条件;
2,程序必须朝基本条件运行。
f(n)=f(n-1)+f(n-2)必须要有基本条件,否则将无从算起,这个基本条件就是:f(0)=0,f(1)=1。这一点,基本上初学者不会犯错。
第二点,是最容易犯错误的:程序必须朝基本条件运行!试看下面这个程序:
int Bad(unsigned int N)
{
if(N==0)
return 0;
else return Bad(N/3+1)+N-1;
}
该程序摘自Mark Allen Weiss 的《Data structures and algorithm analaysis in C》。
是的,它有基本条件,可是,它会朝着这个基本条件运行吗?计算Bad(1)试试看,很明显
Bad(N/3+1)+N-1=Bad(1)+0 !!!
也就是说,这个程序若是要计算Bad(1)那么它将会原地踏步!由于Bad(1)无法计算出来,那么Bad(2)、Bad(3)……都无法计算出来!!
因此,我们为了注意不要犯这个错误,我们引入第三个准则:
3,每次递归调用,都必须向基本条件前进。
很明显,如果调用的就是基本条件时的情况,那么我们不应该再递归了,因此有第四个准则:
4,如果结果已知,那么,不用再重复调用递归。
3、4条准则似乎很多余,但是它却是提醒我们不要犯在写递归调用程序时容易犯的错误。
递归的应用
递归有什么用呢?其实,递归并不是个好算法,因为在有多次(实际情况很可能是许多许多次)调用递归的情况下,会非常耗内存。因此,我们使用递归算法,必须权衡运行时间与内存这两者的消耗,一般来说,递归算法不适合解决数学计算!当然,我们以后会讲到更好的算法,使得运行时间与内存都比较少的情况下解决问题!
具体来说,一般递归用于关于二叉树的算法。
以下程序是关于构造、遍历二叉树的,其中使用了递归算法:
同学们,看得懂吗?OK,不懂的下课问!
今天的课就讲到这里,下课!
下次课我讲最常用的五种算法之一:贪心算法,并且用它来解决实际问题。
今天开始,我将讲解算法大总结。那么让我们开始吧。首先,我今天要讲的是:递归算法。
我觉得讲课要讲得透彻,必须让学生知其所以然。所以,我第一要讲的是:为什么需要递归算法?
让我们看下面这个数学式子:f(n)=f(n-1)+f(n-2),f(0)=0,f(1)=1;
非常明显,若我们想求出f(N),如果不使用递归,那么就必须使用烦琐的嵌套for语句,更要命的是,当N→∞时,for语句将会变成程序的瓶颈,因为它需要大量时间计算!!最终很可能无法“得到”答案。
如果使用递归,那么,程序在时间上的开销将大大降低,当然,其内存消耗会比前面的嵌套for语句要多。
在时间、内存权衡情况下,我们往往宁愿多牺牲内存,毕竟,扩充内存总比等待很长(有时甚至无法“解出”答案)时间才得出答案要好!
那么,我们如何写递归算法呢?
拿上面所提到的数学式子来说,其递归算法是这么写出的:
long function(int N)
{
if(N==0) return 0;
if(N==1) return 1;
if(N>1)
return (function(N-1)+function(N-2));
return -1;
}
看上去很简单,不过却包含了写递归算法的准则:
1,必须要有基本条件;
2,程序必须朝基本条件运行。
f(n)=f(n-1)+f(n-2)必须要有基本条件,否则将无从算起,这个基本条件就是:f(0)=0,f(1)=1。这一点,基本上初学者不会犯错。
第二点,是最容易犯错误的:程序必须朝基本条件运行!试看下面这个程序:
int Bad(unsigned int N)
{
if(N==0)
return 0;
else return Bad(N/3+1)+N-1;
}
该程序摘自Mark Allen Weiss 的《Data structures and algorithm analaysis in C》。
是的,它有基本条件,可是,它会朝着这个基本条件运行吗?计算Bad(1)试试看,很明显
Bad(N/3+1)+N-1=Bad(1)+0 !!!
也就是说,这个程序若是要计算Bad(1)那么它将会原地踏步!由于Bad(1)无法计算出来,那么Bad(2)、Bad(3)……都无法计算出来!!
因此,我们为了注意不要犯这个错误,我们引入第三个准则:
3,每次递归调用,都必须向基本条件前进。
很明显,如果调用的就是基本条件时的情况,那么我们不应该再递归了,因此有第四个准则:
4,如果结果已知,那么,不用再重复调用递归。
3、4条准则似乎很多余,但是它却是提醒我们不要犯在写递归调用程序时容易犯的错误。
递归的应用
递归有什么用呢?其实,递归并不是个好算法,因为在有多次(实际情况很可能是许多许多次)调用递归的情况下,会非常耗内存。因此,我们使用递归算法,必须权衡运行时间与内存这两者的消耗,一般来说,递归算法不适合解决数学计算!当然,我们以后会讲到更好的算法,使得运行时间与内存都比较少的情况下解决问题!
具体来说,一般递归用于关于二叉树的算法。
以下程序是关于构造、遍历二叉树的,其中使用了递归算法:
#include<stdio.h>
typedef struct node * tree;
struct node
{
int number;
tree left;
tree right;
};
tree createTree();
void travelTree(tree );
int main()
{
tree root;
root=createTree();
travelTree(root);
getch();
return 0;
}
tree createTree()
{
int n;
tree root;
root=(tree)malloc(sizeof(struct node));
if(root==NULL) exit(1);
printf("intput a number :");
scanf("%d",&n);
if(n==-1)
{
return NULL;
}
else
{
root->number=n;
root->left=createTree();
root->right=createTree();;
}
return root;
}
void travelTree(tree r)
{
if(r!=NULL)
{
printf("%d ",r->number);
travelTree(r->left);
travelTree(r->right);//此处按先序遍历
}
}
同学们,看得懂吗?OK,不懂的下课问!
今天的课就讲到这里,下课!
下次课我讲最常用的五种算法之一:贪心算法,并且用它来解决实际问题。