关于递归
递归算法的设计思路
Recursive_SOLVE (P) //P是当前问题 if P的规模足够小 then 直接求解 else 将P转换为规模较小问题P' Recursive_SOLVE(P') //递归调用 将P'还原成为原问题P,结果合并 endif END Recursive_SOLVE
- 递归的低效率
- 递归调用实际上是函数自己在调用自己,而函数的调用开销是很大的,系统要为每次函数调用分配存储空间,并将调用点压栈予以记录。而在函数调用结束后,还要释放空间,弹栈恢复断点。所以说,函数调用不仅浪费空间,还浪费时间。
- 递归计算的过程可能会造成重复计算。
低效率的解决方法
- 改进方法:保存中间结果(将递归转化为递推),如下:
递归:递归是一种从上至下的“分解求解“的过程,即不断地把大问题分解为小问题,直到小问题规模足够小,然后求解并进行递归返回和结果合并。——效率差!
递推:递推是一种从下往上的“合并求解“过程,即从解决小问题出发,记录小问题的答案,并根据已有的小问题的答案,把问题往大里扩展,“滚雪球”,直到达到大问题的规模为止。并通常用迭代(循环)的方式实现,而不是递归调用,一般认为效率上比递归好。 - 递推算法的设计思想
将递归转化为递推,需要解决如下几个问题:
1.如何体现递归反映出来的、通过调用自身实现的重复计算——从程序入口重复执行本程序的代码的过程?
2.每次递归完成后要接着递归点往下继续执行,递推时如何记录重复执行点,以实现与递归类似的、在重复点重复执行、执行完后又能接着重复点之后的代码继续执行?
3.如果递归函数有返回值,返回值需要返回至递归调用点,用迭代实现时,如何处理返回值?(小问题的解数组ans)
Recurrence_SOLVE(P) //P是当前问题 定义数组ans[P] //定义一个与问题规模P相适应的数组,用于存放答案 for i from 边界问题P0 to P do //从小问题求解做起 if i是边界值 then 直接求解,将结果保存在ans中 else 将i转换为规模较小问题i' ans[i] ←build answer by ans[i'] //用已得到的答案构造i的解ans[i] endif repeat END Recurrence_SOLVE
- 改进方法:保存中间结果(将递归转化为递推),如下:
- 递归式及其求解
- 递归式
递归式是一组等式或不等式,它所描述的函数是用在更小的输入下该函数的值来定义的。
如:归并排序(MERGESORT)的运行时间T(n)表示如下:
- 顺便说一下递推式(类似于我们平常说的数列)
通过给出数列的第1项(或前若干项),并给出数列的某一项与它的前一项(或前若干项)的关系式来表示数列,这种表示数列的式子叫做这个数列的递推公式。
1.斐波纳契数列的递推公式为fn=fn-1+fn-2
2.等差数列递推公式:an=an-1+d
3.等比数列递推公式:bn=bn-1×q
- 顺便说一下递推式(类似于我们平常说的数列)
- 递推式与递归式之间的联系
当递推式中只含数列中的项,而无常数项或其它项时,就叫做递归公式。 - 递归式的求解
求解递归式就是化简递归式,以得到形式简单的函数表示。三种常用方法:
1.代换法
2.递归树法
3.主方法
- 递归式
关于二叉树的几个递归操作
//搜索
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
typedef int ElementType; // 类型的重命名
typedef struct TreeNode *Position;
typedef struct TreeNode *Tree;
struct TreeNode
{
ElementType Element;
Tree Left;
Tree Right;
}; // struct需要分号
char str[101] = "abc##de#g##f###";
int count_temp = 0;
/* 创建二叉树
* 使用双指针的原因:
* 指针本身也是变量的一种,在(单指针)参数的传递中,也是使用传值的方式,
* 也就是说函数只能改变指针指向的内容,无法修改指针的内容。
* 但是,在二叉树中涉及到对节点的递归重建(递归遍历也是一样,都改变了指针的指向),此时就要改变指针的内容来实现对各节点的操作,
* 这就要借助于指针的指针,即使用二重指针,这本来就是一个难点,仔细体会下!
*/
void createBtree(Tree *T)
{
if (str[count_temp++] == '#')
{
*T = NULL;
}
else
{
*T = (Tree )malloc(sizeof(TreeNode));
(*T)->Element = str[count_temp - 1];
createBtree(&(*T)->Left);
createBtree(&(*T)->Right);
}
}
/*
* 1. 如果二叉树为空,而返回0
* 2. 如果二叉树不为空,二叉树节点树=左子树节点个数+右子树节点个数+1
*/
int GetNodeNum(Tree T)
{
if( T == NULL)//递归出口
{
return 0;
}
return GetNodeNum(T->Left) + GetNodeNum(T->Right) + 1;
}
/*
* 1. 如果二叉树为空,二叉树的深度为0
* 2. 如果二叉树不为空,二叉树的深度 = max(左子树深度,右子树深度)+ 1;
*/
int GetDepth(Tree T)
{
if( T == NULL)//
{
return 0;
}
int LeftDepth = GetDepth(T->Left);
int RightDepth = GetDepth(T->Right);
return LeftDepth > RightDepth ? (LeftDepth+1) : (RightDepth+1);
}
//先序遍历二叉树
void preOrder(Tree T)
{
if( T != NULL)
{
printf("%c ",T->Element);
preOrder(T->Left);
preOrder(T->Right);
}
}
//中序遍历二叉树
void inOrder(Tree T)
{
if( T != NULL)
{
inOrder(T->Left);
printf("%c ",T->Element);
inOrder(T->Right);
}
}
//后续遍历二叉树
void postOrder(Tree T)
{
if( T != NULL)
{
postOrder(T->Left);
postOrder(T->Right);
printf("%c ",T->Element);
}
}
/*
* 1. 如果二叉树为空,或者k<1返回0
* 2. 如果二叉树不为空并且k==1,返回1;
* 3. 如果二叉树不为空且k>1,返回左子树中k-1层的节点个数与右子树k-1层节点个数之和。
*/
int GetNodeNumKthLevel(Tree T, int k)
{
if( T==NULL || k<1)
return 0;
if( k == 1)
return 1;
int numLeft = GetNodeNumKthLevel(T->Left, k-1);
int numRight= GetNodeNumKthLevel(T->Right,k-1);
return (numLeft + numRight);
}
/*
* 1. 如果二叉树为空,或者k<1返回0
* 2. 如果二叉树不为空且左右子树为空,返回1
* 3. 如果二叉树不为空且左右子树不为空,返回左子树叶子节点个数加上右子树叶子节点个数。
*/
int GetLeafNodeNum( Tree T)
{
if( T == NULL )
return 0;
if( T->Left == NULL && T->Right == NULL)
return 1;
int numLeft = GetLeafNodeNum( T->Left);
int numRight = GetLeafNodeNum(T->Right);
return (numLeft + numRight);
}
/*
* 1. 如果两棵二叉树都为空,返回真
* 2. 如果两棵二叉树为空,另一棵不为空,返回假
* 3. 如果两棵二叉树都不为空,如果对应的左子树和右子树都同构返回真,其他返回假
*/
bool StructureCmp(Tree T1, Tree T2)
{
if( T1 == NULL && T2 == NULL)//都为空
return true;
else
if( T1 == NULL || T2 == NULL)//一个空,一个非空
return false;
bool left = StructureCmp(T1->Left, T2->Left);
bool right =StructureCmp(T2->Right,T2->Right);
return (left&right);
}
/*
* 1. 如果二叉树为空,返回真
* 2. 如果二叉树非空,如果左右子树都是AVL树并且左右子树高度相差不大于1,返回真,其他返回假。
*/
bool IsAVL(Tree T, int* height)
{
if(T == NULL )
{
height = 0;
return true;
}
int heightLeft,heightRight;
bool left = IsAVL(T->Left, &heightLeft);
bool right =IsAVL(T->Right,&heightRight);
if( left && right && abs(heightLeft-heightRight)<=1)
{
*height = max(heightLeft, heightRight) + 1;
return true;
}
else
{
*height = max( heightLeft, heightRight) + 1;
return false;
}
}
/*
* 将当前二叉树转化为其镜像二叉树
*/
Tree Mirror(Tree T)
{
if( T == NULL )
return 0;
if( T->Left == NULL && T->Right == NULL)
return 0;
Position temp = T->Right;
T->Right = T->Left;
T->Left = temp;
Mirror(T->Left);
Mirror(T->Right);
}
/*
* 如果二叉树为空,返回0,即同时记录左子树和右子树的深度——0;
* 如果二叉树不为空,最大距离要么是左子树中的最大距离,要么是右子树的最大距离,
* 要么是 左子树节点到根节点的最大距离+右子树节点中到根节点的最大距离,同时记录左右子树节点中到 根节点的最大距离。
*/
int GetMaxDistance(Tree T, int *maxLeft, int *maxRight)
{
// maxLeft:左子树中节点距离根节点最远距离
// maxRight:右子树中节点距离根节点最远距离
if( T == NULL)
{
maxLeft = 0;
maxRight = 0;
return 0;
}
int maxLL, maxLR, maxRL, maxRR;
int maxDistLeft, maxDistRight;
if( T->Left != NULL)
{
maxDistLeft = GetMaxDistance(T->Left, &maxLL, &maxLR);
*maxLeft = max(maxLL, maxLR) + 1;
}
else
{
maxDistLeft = 0;
*maxLeft = 0;
}
if( T->Right != NULL)
{
maxDistRight = GetMaxDistance( T->Right, &maxRL, &maxRR);
*maxRight = max( maxRL, maxRR) + 1;
}
else
{
maxDistRight = 0;
*maxRight = 0;
}
return max( max(maxDistLeft, maxDistRight), *maxLeft+*maxRight);
}
int main()
{
Tree T;
int temp;
/* 创建二叉树 */
createBtree(&T); // 参数是**TreeNode
/* 求解二叉树中的节点个数 */
temp = GetNodeNum(T);
printf("二叉树的节点个数:%d\n", temp);
/* 求解二叉树的深度 */
temp = GetDepth(T);
printf("二叉树的深度:%d\n", temp);
/* 遍历 */
printf("前序遍历(递归):");
preOrder(T);
printf("\n");
printf("中序遍历(递归):");
inOrder(T);
printf("\n");
printf("后序遍历(递归):");
postOrder(T);
printf("\n");
// 求二叉树第k层节点的个数
temp = GetNodeNumKthLevel(T, 2);
printf("二叉树第k层节点的个数:%d\n", temp);
// 求二叉树中叶子节点的个数
temp = GetLeafNodeNum(T);
printf("二叉树中叶子节点的个数:%d\n", temp);
// 判断二叉树结构是否相同相
strcpy(str, "abc##de#g##f###");
count_temp = 0;
Tree T0;
createBtree(&T0);
if(StructureCmp(T, T0))
{
printf("两个二叉树的结构相同\n");
}
else
{
printf("两个二叉树的结构不同\n");
}
// 判断二叉树是不是平衡二叉树
int height;
if(IsAVL(T, &height))
{
printf("该二叉树是平衡二叉树\n");
}
else
{
printf("该二叉树不是平衡二叉树\n");
}
// 求二叉树的镜像二叉树
Mirror(T);
printf("镜像二叉树的后序遍历(递归):");
postOrder(T);
printf("\n");
// 求二叉树中节点的最大距离(二叉树中相距最远的两个节点)
int maxleft, maxright;
temp = GetMaxDistance(T, &maxleft, &maxright);
printf("二叉树中节点距离最大为:%d", temp);
system("pause");
return 0;
}
结果如下: