算法分析---分治法知识点

递归
函数的调用、返回、递归等都需要使用栈这种数据结构。在函数调用时,需要确保在逐级调用后能够回归到最初的调用点,编译器会隐式实现一个堆栈,用来保存每一级函数调用时返回的函数返回值和地址

//青蛙跳问题 青蛙只能跳一阶或二阶 设有n解台阶
//问有多少种跳法?
//分析:如果跳到了n级台阶只能从n-1跳2步上去或从n-2级跳1步上去
int Jump(int n)
{
if(n<1) return 0;
if(n==1) return 1;
if(n==2) return 2;
return Jump(n-1)+Jump(n-2);
//时间复杂度估计为O(2^n)
//根据递归树进行计算
}

尾递归法:如果一个函数中所有递归形式的调用都出现在函数的末尾,当函数递归调用的是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。尾递归的特点是在回归过程中不用做任何操作(函数返回时候,调用自身本身,并且return语句不能包含表达式)

//斐波那契数列  1 1 2 3 5 8
long long Fib(long long first,long long second,int N)
{
if(N<3) return 1;
if(N==3) return first+second;
return Fib(second,first+second,N-1);
//f(n)=f(n-1)+f(n-2)
}
//时间复杂度为O(n)
//斐波那契递推 1 2 3 5 8
long Fib(int n)
{
 if(n==1) return 1;
 if(n==2) return 2;
 
 int result=0;
 int pre=1;
 int end=2;
 for(int i=3;i<=n;i++)
 {
  result=pre+end;
  pre=end;
  end=result;
 }
   return result;
}
//时间复杂度O(n)

奶牛生子问题 一只刚出生的奶牛,4年生一只奶牛,以后每年生一只
现在给你一只刚出生的奶牛,问20年后有多少只奶牛

#include <iostream>
using namespace std;
CalCowsNum(int n)
{
    int iCnt=0;
    int Cout=1;
    for(iCnt=1;iCnt<=n;iCnt++)
    {
        if(iCnt>=4)//第四年开始生羊
        {
            if((n-iCnt)>3)
                **//第二个四年之后产的奶牛开始生**
                //分解子问题
                Cout+=CalCowsNum(n-iCnt);
        }
        else
            Cout++;
    }
    return Cout;
}
int main()
{
    int k=CalCowsNum(20);
    cout<<k;
    return 0;
}

注意:
1.子问题与原问题形式相同
2.子问题的规模小到一定程度可以容易的直接求解
3.分解后的子问题可以合并 进而合并求解
4.子问题可以彼此独立的求解,即子问题不包含公共的子问题

分治法一般算法框架(伪代码)
Divide-and-Conquer§
1. if |P|≤n0
2. then return(ADHOC§)  
3. 将P分解为较小的子问题 P1 ,P2 ,…,Pk  
4. for i=1 to k  
5. do yi = Divide-and-Conquer(Pi) //递归解决Pi  
6. T = MERGE(y1,y2,…,yk) //合并子问题  
7. return(T)
其中|P|表示问题P的规模;n0为一阈值,表示当问题P的规模不超过n0时,问题已容易直接解出,不必再继续分解。ADHOC§是该分治法中的基本子算法,用于直接解小规模的问题P。因此,当P的规模不超过n0时直接用算法ADHOC§求解。算法MERGE(y1,y2,…,yk)是该分治法中的合并子算法,用于将P的子问题P1 ,P2 ,…,Pk的相应的解y1,y2,…,yk合并为P的解。

典型分治法:
①典型分治法的递归方程(二分法)
T(n)=2T(n/2)+f(n)
②减治法 把一个大的问题,转化为若干个子问题,这些子问题中只解决一个,大的问题便随之解决

发布了19 篇原创文章 · 获赞 2 · 访问量 744

猜你喜欢

转载自blog.csdn.net/qq_45639157/article/details/104779104