递归简介

关于递归
  • 递归算法的设计思路

    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;
}

结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值