赞
踩
什么是递归?
自相似的方法
重复进行的过程递归的过程:先自顶向下找到递归出口,在自底向上回到最初的递归位置
推导路径未知的题目只能用递归不能用循环
比如求多叉树的节点(每个节点的分支都不同)
推导路径未知的题目只能用递归不能用循环
比如求多叉树的节点(每个节点的分支都不同)
一般来说,涉及全局变量和静态变量或者类私有变量,不同层之间可能需要进行数据恢复
下图是长度为2的二进制个数
- 从左至右,依次加入0,回溯时放弃0,选择1
这种方法一定要传递供选项,就是每次进行的选择
vector<vector<int>> res; void getAllBin(vector<int> nums,vector<int> ans,int n){ for (int i = 0; i < nums.size();i++){ ans.push_back(nums[i]);//先添加元素 if(ans.size()==n){//满足存结果,不要return res.push_back(ans); } else{//不满足长度递归 getAllBin(nums, ans, n); } ans.pop_back(); } } vector<vector<int>> printAllbin(int n){ vector<int> nums = {0, 1};//显式给出01供选项 vector<int> ans; getAllBin(nums, ans, n); return res; }
当然也可以把判断语句放在if外,需要return
void getAllBin(vector<int> nums,vector<int> ans,int n){ if(ans.size()==n){//满足存结果 res.push_back(ans); return; } for (int i = 0; i < nums.size();i++){ ans.push_back(nums[i]);//先添加元素 //递归 getAllBin(nums, ans, n); ans.pop_back(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
一般选择这种,更清晰
vector<vector<int>> res; void getAllBin(vector<int> ans,int n){ if(ans.size()==n){ res.push_back(ans); return; } //加入0回溯 vector<int> tmp1 = ans; tmp1.push_back(0); getAllBin(tmp1, n); //加入1回溯 vector<int> tmp2 = ans; tmp2.push_back(1); getAllBin(tmp2, n); } vector<vector<int>> printAllbin(int n){ vector<int> ans; getAllBin(ans, n); return res; }
这里是传入新变量,当然也可以用ans,但是需要pop_back(),防止影响结果
void getAllBin(vector<int> ans,int n){ if(ans.size()==n){ res.push_back(ans); return; } //加入0回溯 ans.push_back(0); getAllBin(ans, n); //别忘了pop_back() ans.pop_back(); // 加入1回溯 ans.push_back(1); getAllBin(ans, n); //pop与否都可以 //ans.pop_back(); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
这里ans.size()
是隐式递归结束变量,也可以单独用一个参数,但是要注意递归+1
vector<vector<int>> res; void getAllBin(vector<int> ans,int n,int level){ if(level==n){ res.push_back(ans); return; } //加入0回溯 ans.push_back(0); getAllBin(ans, n, level + 1);//+1 ans.pop_back(); // 加入1回溯 ans.push_back(1); getAllBin(ans, n, level + 1);//+1 //pop与否都可以 return; } vector<vector<int>> printAllbin(int n){ vector<int> ans; getAllBin(ans, n,0);//从0开始 return res; }
当然也可以用减法实现,size==0一样可以结束递归
vector<vector<int>> res; void getAllBin(vector<int> ans,int n){ if(n==0){ res.push_back(ans); return; } //加入0回溯 ans.push_back(0); getAllBin(ans, n-1);//+1 ans.pop_back(); // 加入1回溯 ans.push_back(1); getAllBin(ans, n-1);//+1 //pop与否都可以 return; } vector<vector<int>> printAllbin(int n){ vector<int> ans; getAllBin(ans, n); return res; }
bst
数据结构struct bst{
//节点,左子树,右子树
int val;
bst *left;
bst *right;
//带有默认参数的构造函数
bst(int v = 0,bst* l=NULL,bst* r=NULL):val(v),left(l),right(r){}
};
void midTraverse(const bst* root){
if(root==NULL)
return;
midTraverse(root->left);
cout << root->val << " ";
midTraverse(root->right);
}
int main(){
bst *aa = new bst(100);
bst *bb = new bst(500);
bst *cc = new bst(200, aa, NULL);
bst *dd = new bst(400, NULL, bb);
bst *ee = new bst(300,cc,dd);
midTraverse(ee);
return 0;
}
运行结果:
100 200 300 400 500
bst* createBST_loop(int a[],int n){ //首先根节点设为第一个元素值 bst *root = new bst(a[0]); //插入余下值 bst *parent = NULL; for (int i = 1; i < n;i++){ //定义指向root节点,防止断链 bst *cur = root;//必须从root开始 // 先找到插入值的父节点,再进行插值 while (cur) { if (a[i] < cur->val) { parent = cur; cur = cur->left; } else if (a[i] > cur->val) { parent = cur; cur = cur->right; } else { // a[i]==root->val return root; } } //判断当前值和父节点大小来将新节点插入到左右子树 if(parent){//防止NULL->__ if(a[i]<parent->val){ parent->left = new bst(a[i]); } else{ parent->right = new bst(a[i]); } } }//for return root; }
当然也可以把根节点作为参数,函数没有返回值
但是必须传入指针的引用
传引用,初始化必须有指向,比如NULL
void create_BST_loop(bst*& root,int a[],int n){ root = new bst(a[0]); bst *parent = NULL; for (int i = 1; i < n;i++){ bst *cur = root; while(cur){ if(a[i]<cur->val){ parent = cur; cur = cur->left; } else if(a[i]>cur->val){ parent = cur; cur = cur->right; } else{ return; } } //待插节点 bst *tmp = new bst(a[i]); if(parent){ if(a[i]<parent->val){ parent->left = tmp; } else{ parent->right = tmp; } } }//for }
bst *newnode=NULL;
create_BST_loop(newnode, a, n);
midTraverse(newnode);
bst* create_BST_loop(int a[],int n){ bst* root = new bst(a[0]); for (int i = 1; i < n;i++){ bst *cur = root; while(cur){ if(a[i]<cur->val){ if(cur->left){ cur = cur->left; } else{ cur->left = new bst(a[i]); break;//插入后跳出 } } else if(a[i]>cur->val){ if(cur->right){ cur = cur->right; } else{ cur->right = new bst(a[i]); break; } } else{ return root; } } }//for return root; }
这里不需要父节点了,只要往左右子树遍历时判断一下存在性
不存在说明该左子树就是要插值
千万别忘了插入后的break
由于每次插入需要找到插入位置,所以用递归找到插入位置,然后循环多次插入
void getRecursion(bst*& root,int x){ if(root==NULL){ root = new bst(x); return; } if(x<root->val){ getRecursion(root->left, x); } else if(x>root->val){ getRecursion(root->right, x); } else{//== return; } } bst* create_BSTrecursion(int a[],int n){ bst *root = new bst(a[0]); for (int i = 0; i < n;i++){ int x = a[i]; getRecursion(root, x); } return root; }
bst* getRecursion(bst* root,int x){ if(root==NULL){ root = new bst(x); return root; } if(x<root->val){//自己建立链条 root->left = getRecursion(root->left, x); } else if(x>root->val){ root->right = getRecursion(root->right, x); } return root; } bst* create_BSTrecursion(int a[],int n){ bst *root = new bst(a[0]); // bst* tmp; for (int i = 0; i < n;i++){ int x = a[i]; getRecursion(root, x); //tmp=getRecursion(root, x); } return root; //return tmp }
bst* create_BSTrecursion(int a[],int n){ bst *root = new bst(a[0]); bst* tmp; for (int i = 0; i < n;i++){ int x = a[i]; tmp=getRecursion(root, x); } return tmp }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
可以定义新变量接受返回值,因为root在函数内部,可以不写
root=getRecyrsion();
void getRecursion(bst* root,bst* parent,int x){ if(root==NULL){ /*建立parent与root联系*/ if(x<parent->val){ parent->left = new bst(x); } else{ parent->right = new bst(x); } return;//递归结束 } //传递父节点 if(x<root->val){ getRecursion(root->left,root, x); } else if(x>root->val){ getRecursion(root->right,root,x); } return; } bst* create_BSTrecursion(int a[],int n){ bst *root = new bst(a[0]); for (int i = 0; i < n;i++){ int x = a[i]; getRecursion(root,NULL, x); } return root; }
bst不可以有重复值,所以生成随机数时候需要去重
int n = 10;
int *a = new int[10];
for (int i = 0; i < 10;i++){
//生成[a,b)的随机数---(rand()%(b-a))+a
a[i] = (rand() % 90)+10;
//bst不能有重复值,相等去除
for (int j = 0; j < i;j++){
if(a[j]==a[i]){
i-=1;//重新生成a[i]
break;
}
}
}
// bst *root = create_BSTrecursion(a, n);
也就是变动原数组,选择一个就把自己的选择去掉,然后递归,回溯的时候补上元素
把数组恢复需要用到insert()函数,需要传递插入位置和插入值:
所以在删除的时候提前记录删除元素值和删除的iter
位置
vector<vector<int>> res; void getLine(vector<int> nums ,vector<int> ans,int n){ if(n==0){ res.push_back(ans); return; } for (int i = 0; i < nums.size();i++){ //记录删除元素x int x = nums[i]; ans.push_back(nums[i]); //记录删除元素位置的下一个迭代器it auto it=nums.erase(nums.begin()+i); getLine(nums, ans, n - 1); //别忘了回溯撤销ans的选择 ans.pop_back(); //恢复原数组,在it前加入x nums.insert(it,x); } } vector<vector<int>> fullLine(vector<int> nums){ vector<int> tmp; getLine(nums, tmp,nums.size()); return res; }
先选元素,再回溯
由于子集中元素可选可不选,每次循环的开始值是上一次的值+1
class Solution { public: vector<vector<int>> subsets(vector<int>& nums) { vector<int> mm; findSub(nums, mm, 0); res.push_back({}); return res; } private: vector<vector<int>> res; void findSub(vector<int> nums, vector<int> ans, int index) { for (int i = index; i < nums.size(); i++) { ans.push_back(nums[i]); // res.push_back(ans); findSub(nums, ans, i + 1); // 当前i的下一个 res.push_back(ans); // 在递归前记录也行 ans.pop_back(); } } };
运行结果:
77. 组合
C
n
k
:
从
n
个元素中选
k
个
C_n^k:\text{从}n\text{个元素中选}k\text{个}
Cnk:从n个元素中选k个
其实思路类似子集:
比如{1,2,3}中{1,3}和{3,1}是重复的,所以元素只能以该元素为起点,向后递归,不能往前选
但是组合规定了个数,只要在结果处存储正确的符合条件的结果即可,在所有路径提前剪去不符合要求的
注意:
从i开始的,必须要求[i,...,n]的个数n-i+1加上ans当前的数量大于k
即:
n-i+1+ans.size()>k
----数量小于k一点不符合题意,提前剪枝
class Solution { public: vector<vector<int>> combine(int n, int k) { this->n=n; this->k=k; vector<int> mm; findCombine(mm,0); return res; } private: int n; int k; vector<vector<int>> res; void findCombine(vector<int> ans,int index){ if(ans.size()==k){ res.push_back(ans); return;//递归结束 } //[i,...,n]个数:n-i+1+ans.size()<k for(int i=index;i<(n-k+1)+ans.size();i++){ ans.push_back(i+1); findCombine(ans,i+1); ans.pop_back(); } } };
也可以按照选不选元素去回溯,但是要提前结束
- 当前ans的大小超过k了
- 当前ans的大小把后面全选了也不足k
class Solution { public: vector<vector<int>> combine(int n, int k) { this->n = n; this->k = k; vector<int> mm; findCombine(mm, 1); return res; } private: int n; int k; vector<vector<int>> res; void findCombine(vector<int> ans, int index) { // ans.size()+(n-index+1)<k if (ans.size() > k || ans.size() + (n - index + 1) < k) { return; // 提前回溯 } if (ans.size() == k) { res.push_back(ans); return; } // 选该元素 ans.push_back(index); findCombine(ans, index + 1); // 不选该元素 ans.pop_back(); findCombine(ans, index + 1); } };
// 不选该元素 findCombine(ans, index + 1); // 选该元素 ans.push_back(index); findCombine(ans, index + 1);
- 1
- 2
- 3
- 4
- 5
从不选到选,不需要pop_back(),加上也正确
分为排列,子集,组合,后一种分别是前一种的子集
类似问题只需在该基础上进行剪枝
分治求法:
int maxDepth(TreeNode* root) {
if(root==NULL) return 0;
int l=maxDepth(root->left);
int r=maxDepth(root->right);
return max(l,r)+1;
}
联系在一起的关键是:函数体内求左子树和求右子树的对应一个root
这是典型的自底向上,也就是相当于从下面向前面追溯得到结果
class Solution { public: int maxDepth(TreeNode* root) { res = 0; dfs(root, 1);//根节点为1 return res; } private: int res; void dfs(TreeNode* root, int depth) { if (root == NULL) return; dfs(root->left, depth + 1); dfs(root->right, depth + 1); res = max(res, depth); // 更新结果 } };
每次递归依次就更新依次结果
递归前或递归后递归不影响
res = max(res, depth); // 更新结果 dfs(root->left, depth + 1); dfs(root->right, depth + 1);
- 1
- 2
- 3
当然也可以把depth设为全局变量,但是要清理现场
class Solution { public: int maxDepth(TreeNode* root) { res = 0; depth=0; dfs(root); return res; } private: int res; int depth; void dfs(TreeNode* root) { if (root == NULL) return; depth++;//当前层+1 res = max(res, depth); // 更新结果 dfs(root->left); //depth--; //depth++; dfs(root->right); depth--;//回到上一层,depth-1 } };
队列存储二元组,每次都记录层次大小
using pr=pair<TreeNode*,int>; int maxDepth(TreeNode* root) { int res=0; /*广度优先遍历*/ queue<pr> que; int level=1;//记录层次 if(root==NULL) return 0; que.push(make_pair(root,level)); while(!que.empty()){ TreeNode* tmp=que.front().first; int level=que.front().second; que.pop(); res=max(res,level);//更新res if(tmp->left) que.push(make_pair(tmp->left,level+1)); if(tmp->right) que.push(make_pair(tmp->right,level+1)); } return res; }
当然也可以利用队列每次存的都是当前层的所有元素
int maxDepth(TreeNode* root) { int res = 0; /*广度优先遍历*/ queue<TreeNode*> que; int level = 1; // 记录层次 if (root == NULL) return 0; que.push(root); while (!que.empty()) { int sz = que.size(); // 当前队列元素,当前层个数 while (sz > 0) {//当前层所有元素出队 TreeNode* tmp = que.front(); que.pop(); if (tmp->left) que.push(tmp->left); if (tmp->right) que.push(tmp->right); sz--; } res+=1;//层次+1 } return res }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。