算法设计与分析课程记录week2
PS:此文章仅作为个人课程期间的学习记录
文章目录
递归算法设计技术
1. 递归的概念
在定义一个过程或函数时出现调用本过程或本函数的成分,称之为递归。若调用自身,称之为直接递归。若过程或函数p调用过程或函数q,而q又调用p,称之为间接递归。
能够用递归解决问题的三个条件:
- 需要解决的问题可以转化为一个或多个子问题来求解,而这些子问题的求解方法与原问题完全相同,只是在数量规模上不同。
- 递归调用的次数必须是有限的。
- 必须有出口来结束递归的条件来终止递归。
何时使用递归?
- 定义是递归的
如:求n!和Fibonacci数列等 - 数据结构是递归的
如:单链表,二叉树等 - 问题的求解方法是递归的
如:汉诺塔问题
不建议在太大的数据量使用递归的原因
递归调用是函数嵌套调用的一种特殊情况,由于每次调用时,它的参量和局部变量均不相同,因而得保证了各个复制件执行时的独立性。故系统为每一次调用开辟一组存储单元,用来存放本次调用的返回地址以及被中断的函数的参量值。
这些单元以系统栈的形式存放,每调用一次进栈一次,当返回时执行出栈操作,把当前栈顶保留的值送回相应的参量中进行恢复,并按栈顶中的返回地址,从断点继续执行。具体看下图例子。
从以上过程可以得知:
- 每递归调用一次,就需进栈一次,最多的进栈元素个数称为递归深度,当n越大,递归深度越深,开辟的栈空间也越大。
- 每当遇到递归出口或完成本次执行时,需退栈一次,并恢复参量值,当全部执行完毕时,栈应为空。
2.递归算法设计
数学归纳法是一种论证方法,而递归是算法和程序设计的一种实现技术,数学归纳法是递归的基础。
第一与第二数学归纳法:
递归算法设计的一般步骤
递归数据结构的定义:
采用递归方式定义的数据结构称为递归数据结构。在递归数据结构定义中包含的递归运算称为基本递归运算。
单链表的递归算法设计
二叉树的递归算法设计
3. 递归算法设计实例
求解n皇后问题
4.递归算法转化非递归算法
把递归算法转化为非递归算法有如下两种基本方法:
- 直接用循环结构的算法替代递归算法。
- 用栈模拟系统的运行过程,通过分析只保存必须保存的信息,从而用非递归算法替代递归算法。
第一种是直接转化法,不需要使用栈。第二种是间接转化法,需要使用栈。
5.课堂练习
1.利用已给函数练习创建输出删除和复制一棵二叉树,还有求根节点到任意节点的正向与逆向路径。
我所构造的二叉树示意图如下:
代码如下:
//二叉树的基本运算算法+例2.8到例2.11
#include <malloc.h>
#include <stack>
#include <vector>
#include <string>
#include<iostream>
using namespace std;
typedef int ElemType;
typedef struct node
{ ElemType data; //数据元素
struct node *lchild; //指向左孩子结点
struct node *rchild; //指向右孩子结点
} BTNode; //二叉链结点类型
BTNode *CreateBTree(ElemType a[],ElemType b[],int n) //对应例2.8的算法
//由先序序列a[0..n-1]和中序序列b[0..n-1]建立二叉链
{
int k;
if (n<=0) return NULL;
ElemType root=a[0]; //根结点值
BTNode *bt=(BTNode *)malloc(sizeof(BTNode));
bt->data=root;
for (k=0;k<n;k++) //在b中查找b[k]=root的根结点
if (b[k]==root)
break;
bt->lchild=CreateBTree(a+1,b,k); //递归创建左子树
bt->rchild=CreateBTree(a+k+1,b+k+1,n-k-1); //递归创建右子树
return bt;
}
void DispBTree(BTNode *bt) //采用括号表示输出二叉链bt
{
if (bt!=NULL)
{ printf("%c",bt->data);//修改data域为字符型
if (bt->lchild!=NULL || bt->rchild!=NULL)
{ printf("("); //有孩子结点时才输出(
DispBTree(bt->lchild); //递归处理左子树
if (bt->rchild!=NULL) printf(","); //有右孩子结点时才输出,
DispBTree(bt->rchild); //递归处理右子树
printf(")"); //有孩子结点时才输出)
}
}
}
void DestroyBTree(BTNode *&bt) //对应例2.9的算法
//释放以bt为根结点的二叉树
{ if (bt!=NULL)
{ DestroyBTree(bt->lchild);
DestroyBTree(bt->rchild);
free(bt);
}
}
void CopyBTree(BTNode *bt,BTNode *&bt1) //对应例2.10的算法
//由二叉树bt复制产生bt1
{
if (bt==NULL)
bt1=NULL;
else
{
bt1=(BTNode *)malloc(sizeof(BTNode));
bt1->data=bt->data;
CopyBTree(bt->lchild,bt1->lchild);
CopyBTree(bt->rchild,bt1->rchild);
}
}
bool Findxpath1(BTNode *bt,int x,vector<int> &path) //对应例2.11的解法1
//求根结点到x结点的(逆向)路径
{
if (bt==NULL) //空树返回false
return false;
if (bt->data==x) //找到值为x的结点
{
path.push_back(x); //结点值加入path中,并返回true
return true;
}
else if (Findxpath1(bt->lchild,x,path) || Findxpath1(bt->rchild,x,path))
{
path.push_back(bt->data); //结点值加入path中,并返回true
return true;
}
}
bool Findxpath2(BTNode *bt,int x,vector<int> tmppath,vector<int> &path) //对应例2.11的解法2
//求根结点到x结点的(正向)路径
{
if (bt==NULL) //空树返回false
return false;
tmppath.push_back(bt->data); //当前结点加入path
if (bt->data==x) //当前结点值为x,返回true
{
path=tmppath;
return true;
}
bool find=Findxpath2(bt->lchild,x,tmppath,path);//在左子树中查找
if (find) //左子树中成功找到
return true;
else //左子树中没有找到,在右子树中查找
return Findxpath2(bt->rchild,x,tmppath,path);
}
int main()
{
int a[]={'A','B','D','C','E'};
int b[]={'D','B','A','C','E'};
BTNode *bt = CreateBTree(a,b,5);
BTNode *bt1;
//输出创建的树bt
printf("创建的树bt:");
DispBTree(bt);
printf("\n");
CopyBTree(bt,bt1);//复制bt到bt1
DestroyBTree(bt);//销毁创树bt
//输出复制的树bt1
printf("复制的树bt1:");
DispBTree(bt1);
printf("\n");
//创建两个迭代器
vector<int> v;
vector<int> v1;
//逆向输出路径
Findxpath1(bt1,'D',v);
vector<int>::iterator it = v.begin();
cout<<"根节点A到D的逆向输出路径:";
for (; it != v.end(); it++){
printf("%c ",*it);
}
cout<<endl;
//正向输出路径
Findxpath2(bt1,'D',v1,v);
vector<int>::iterator it1 = v.begin();
cout<<"根节点A到D的正向输出路径:";
for (; it1 != v.end(); it1++){
printf("%c ",*it1);
}
return 0;
}
输出结果: