1.递归是在系统的栈区进行调用的,每次调用递归,都会在栈区开辟一块区域来保存此时的递归函
数,当此时的递归函数执行完后,就会释放区域,此时这块区域可以用来调用其他的递归,如前序
遍历二叉树时,当遍历完左边的子树时,左边开辟的栈区可以继续用来调用右边的子树。
2.所有的递归行为都可以改为非递归,递归无非是在系统的栈区进行的,我们完全可以自己在内存
区开辟一个栈,来模拟递归。
3.master公式:
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;
}