递归相关专题 注:master公式

1.递归是在系统的栈区进行调用的,每次调用递归,都会在栈区开辟一块区域来保存此时的递归函

数,当此时的递归函数执行完后,就会释放区域,此时这块区域可以用来调用其他的递归,如前序

遍历二叉树时,当遍历完左边的子树时,左边开辟的栈区可以继续用来调用右边的子树。

2.所有的递归行为都可以改为非递归,递归无非是在系统的栈区进行的,我们完全可以自己在内存

区开辟一个栈,来模拟递归。

3.master公式:

递归相关专题 注:master公式_第1张图片
T(n)=a*T(n/b)+O(n^c);

master公式可以用来求子问题规模相同的递归的时间复杂度

 void dfs(int left,int right){
    int mid=left+((right-left)>>1);
    dfs(left,mid);
    dfs(mid,right+1);
  }

以上面的代码为例,left到right的区间递归时被分为了相等长度的两部分,子问题的规模就变为了原来的1/2,那么b=2;有两个子问题,a=2,每层递归除递归外其他的操作的时间复杂度为O(1),所以T(n)=2*T(n/2)+O(n^0),由此我们就可以估计其时间复杂度。(只要分出的子问题的规模相同就可,如上述的例子中left到right也可以分为2/3和2/3,无非是有一块区间重合,效率受影响,但也可以用master公式来估计其时间复杂度,当然每层递归的其他操作的时间复杂度也不一定是常数级别的)

算法讲解020【必备】递归和master公式

经典递归解析:递归问题大体分为带路径的递归问题VS不带路径的递归问题(包括大部分dp问题,状压dp是简化路径的问题)。任何递归都是dfs。回溯也是递归,所以没必要单独将回溯拿出学习

算法讲解038【必备】常见经典递归过程解析

1.字符串的全部子序列  

字符串的长度为n,则有2^n个子集(子序列),而每个子序列的平均长度为O(N)级别的,生成每个子序列的时间复杂度为O(N),所以其时间复杂度为O(N*2^N)

string path;
     vectorans;
     void get_string(int i,string s){
        ans.push_back(path);
        for(int j=i;j generatePermutation(string s) {
        get_string(0,s);
        setrem(ans.begin(),ans.end());//去重
        ans.clear();

        ans.assign(rem.begin(),rem.end());
        return ans;
    }

2.子集

时间复杂度:最差情况为所有的数都不一样,此时子集的个数为2^n,平均长度为n,时间复杂度为O(N*2^N)

方法1:

vector path;
    vector> ans;
    void f(int i,vectornums){
        ans.push_back(path);
        if(i==nums.size()) return ;
        for(int j=i;j> subsetsWithDup(vector& nums) {
        sort(nums.begin(),nums.end());
        f(0,nums);
        set> rem(ans.begin(),ans.end());
        ans.clear();
        ans.assign(rem.begin(),rem.end());
        return ans;

    }

 方法2:

vector> ans;
    int path[20];//记录
    void f(vector nums, int size, int i) {//size用来记录填入的个数,相当于path用来回溯的
        if (i == nums.size()) {
            vector rem;
            for (int j = 0; j < size; j++)
                rem.push_back(path[j]);
            ans.push_back(rem);
            return;
        }
        int j = 0;
        for (j = i + 1; j < nums.size(); j++) {
            if (nums[j] == nums[i])
                continue;
            else
                break;
        }
        f(nums, size, j);
        while (i != j) {
            path[size++] = nums[i];
            f(nums, size, j);
            i++;
        }
    }
    vector> subsetsWithDup(vector& nums) {
        sort(nums.begin(), nums.end());
        f(nums, 0, 0);
        return ans;
    }

3.全排列l

时间复杂度:一共有n!个排列,每个排列的长度为n,所以时间复杂度为O(N*N!) 

方法1:

vector> ans;
    vector path;
    int rem = 0;
    void f(vector nums) {
        if (path.size() == nums.size()) {
            ans.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            if ((rem >> i) & 1 == 1)
                continue;
            path.push_back(nums[i]);
            rem |= (1 << i);
            f(nums);
            path.pop_back();
            rem &= (~(1 << i));
        }
    }
    vector> permute(vector& nums) {
        f(nums);
        return ans;
    }

方法2:

 vector> ans;
    void swap(vector& nums, int i, int j) {
        int rem = nums[i];
        nums[i] = nums[j];
        nums[j] = rem;
    }
    void f(vector nums, int i) {
        if (i == nums.size()) {//直接复用nums
            ans.push_back(nums);
            return;
        }
        for (int j = i; j < nums.size(); j++) {
            swap(nums, i, j);
            f(nums, i + 1);
            swap(nums, i, j);
        }
    }
    vector> permute(vector& nums) {
        f(nums, 0);
        return ans;
    }

4.全排列ll

时间复杂度为O(N*N!)

方法1:

vector> ans;
    void swap(vector& nums, int i, int j) {
        int rem = nums[i];
        nums[i] = nums[j];
        nums[j] = rem;
    }
    void f(vector nums, int i) {
        if (i == nums.size()) {
            ans.push_back(nums);
            return;
        }
        set map;
        for (int j = i; j < nums.size(); j++) {

            if (map.find(nums[j]) == map.end()) {
                map.insert(nums[j]);
                swap(nums, i, j);
                f(nums, i + 1);
                swap(nums, i, j);
            }
        }
    }
    vector> permuteUnique(vector& nums) {
        sort(nums.begin(), nums.end());
        f(nums, 0);
        return ans;
    }

方法2:

class Solution {
public:
    vector> ans;
    vector path;
    void backtracking(vectornums,vectorused){
        if(path.size()==nums.size()){
            ans.push_back(path);
            return ;
        }
        for(int i=0;i0&&nums[i]==nums[i-1]&&used[i-1]==false)
            continue;
            if(used[i]) continue;
            path.push_back(nums[i]);
            used[i]=true;
            backtracking(nums,used);
            used[i]=false;
            path.pop_back();
        }
    }
    vector> permuteUnique(vector& nums) {
        sort(nums.begin(),nums.end());
        vectorused(nums.size(),false);
        backtracking(nums,used);
        return ans;

    }
};

5.递归翻转栈

时间复杂度:取出最底部的元素的时间复杂度为O(N),所以一共有N个元素,所以时间复杂度为
O(N^2)

 int bottomnum(vector& nums) {//求栈的最底部的元素
        int rem = nums.top();
        if (nums.empty())
            return rem;
        int last = bottomnum(nums);
        nums.push(rem);
        return last;
    }

    void reverse(stack& nums) {
        if (nums.empty())
            return;
        int rem = bottomnum(nums);
        reverse(nums);
        nums.push(rem);
    }

6.递归排序数组:

时间复杂度:每次沉底的时间复杂度为O(N),一共N个数,所以时间复杂度为O(N^2)

 int get_max(stack&nums,int depth){//求从栈顶到最深depth层的最大值
        if(depth==0)
        return INT_MIN;
        int num=nums.top();
        nums.pop();
        int maxnum=get_max(nums,depth-1);
        maxnum=max(num,maxnum);
        nums.push(num);
        return maxnum;
    }

    int times(stack&nums,int depth,int maxnum){//depth对应的最大值出现的次数
        if(depth==0) return ;
        int num=nums.top();
        nums.pop();
        int resttimes=times(nums,depth-1,maxnum);
        int sum=resttimes+(num==maxnum?1:0);
        nums.push(num);
        return sum;
    }

    void dowmnum(stack&nums,int depth,int maxnum,int k){//将最大值沉入栈底
        if(depth==0){
            for(int i=0;i

7.汉诺塔问题

时间复杂度:n层移动需要的步数为f(n)=f(n-1)+1+f(n-1)=2*f(n-1)+1,所以移动的步数为(2^n)-1,其时间复杂度为O(2^N)

#include 
#include 
using namespace std;
void hannuta(int n,string from,string to,string other) {
	if (n == 1) {
		cout << "移动1从" << from << "到" << to<> n;
	hannuta(n,"左","中","右");
	return 0;
}

你可能感兴趣的:(算法)