递归:从入门到入土

1.递归经典入门

1.1 经典入门例子:斐波那契序列

f(n) = f(n - 1) + f(n - 2)

int fibonacci(int n){
	if(n == 0) return 0;
	if(n == 1) return 1;
	return fibonacci(n -1) + fibonacci(n - 2);
}

1.2 求阶乘

f(n) = n * f(n - 1)

int factorial(int n){
	if(n == 0) return 1;
	return n * factorial(n - 1);
}

1.3 gcd

result = gcd(b,a mod b)

int gcd(int a, int b) {//a为被除数,b为除数gcd(b,a mod b)
	if (a % b == 0) return b;
	return (b, a % b);
}

2.分治

2.1.1 跳台阶

n阶台阶,可以走一步,也可以走两步,求一共多少种可能性
解析
Fibonacci : f(n) = f(n - 1) + f(n - 2)

int Fibonacci(int n){
	if( n == 0 ) return 0;
	if( n == 1 ) return 1;
	if( n == 2 ) return 2; 
	return Fibonacci( n - 2 ) + Fibonacci(n - 1);
}

2.1.2 变态跳台阶

n阶台阶,可以走1 - n 步,求一共多少种可能性
Fibonacci : f(n) = 1 + f(n - 1) + f(n - 2) + … + f(1);
递归的深度比较长,使用迭代

int JumpEx(int n) {
	vector<int> p(n+1,1);
	p[0] = 0;
	p[1] = 1;
	p[2] = 2;
	for (int i = 3; i <= n; ++i) 
		for (int j = 1; j < i; ++j) 
			p[i] += p[j];
	return p[n];
}

2.2 归并排序

先归
在这里插入图片描述
后并

void mergeArray(int A[], int lo,int mid, int hi) {
	int* temp = new int[hi - lo + 1]; 
	int i = lo, j = mid + 1;   
	int k = 0;
	while (i <= mid && j <= hi) 
	{
		if (A[i] <= A[j])//注意要包括等号
			temp[k++] = A[i++];       
		else temp[k++] = A[j++];
	}    
	while (i <= mid) 
		temp[k++] = A[i++];    
	while (j <= hi) 
		temp[k++] = A[j++];
	for (int i = lo, k = 0; i <= hi; i++, k++)
	{
		A[i] = temp[k];
	}   
	delete[] temp;
}
void mergeSort(int A[], int lo, int hi) {
	if (lo >= hi) return;    
	int mid = lo + (hi - lo) / 2;  
	mergeSort(A, lo, mid);  //左半区间[lo, mid] 排好序  
	mergeSort(A, mid + 1, hi);  //右半区间[mid + 1, hi] 排好序   
	mergeArray(A, lo, mid, hi); //进行合并 
}

3.二叉树

3.1二叉树的遍历

1.1 先序:递归

void preOrder(TreeNode *root,void (*visit)(TreeNode *p)){
	if( root == nullptr) return;
	visit(root);
	preOrder(root -> left,visit);
	preOrder(root -> left,visit);
}

1.2 先序:迭代

利用栈的特点,永远先入栈右子树,然后入栈左子树,栈顶永远是左子树

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*> s;
        vector<int> res;
        if(root)
            s.push(root);
        while(!s.empty()){
            TreeNode* p = s.top();
            s.pop();
            res.push_back(p -> val);
            if(p -> right) s.push(p -> right);
            if(p -> left)  s.push(p -> left);
        }
        return res;
    }
};

2.1 中序:递归

void preOrder(TreeNode *root,void (*visit)(TreeNode *p)){
	if( root == nullptr) return;
	preOrder(root -> left,visit);
	visit(root);
	preOrder(root -> left,visit);
}

2.2 中序:迭代

基本思想:入栈根节点,从根节点开始,入栈所有左节点
发现:不能入栈了
此时栈顶为最左的节点,访问,出栈
加入其右节点(如果存在)
如果不存在右节点,继续出栈

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ret;
        stack<TreeNode*> s;
        TreeNode* p = root;
        while(p || !s.empty()){//不存在右子树,且当前栈内节点全部遍历过
            while(p) {
                s.push(p);//入栈所有左节点和p
                p = p -> left;
            }
            TreeNode* curr = s.top();
            s.pop();
            ret.push_back(curr -> val);
            p = curr -> right;
        }
        return ret;
    }    
};

3.1 后序:递归

void preOrder(TreeNode *root,void (*visit)(TreeNode *p)){
	if( root == nullptr) return;
	preOrder(root -> left,visit);
	preOrder(root -> left,visit);
	visit(root);
}

3.2 后序:迭代

验证可以通过类似于先序迭代的方法:不同点在于,先入栈左子树,然后右子树,最后将访问的顺序倒置

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> s;
        vector<int> ret;
        if(root) s.push(root);
        while(!s.empty()){
            TreeNode *p = s.top();
            s.pop();
            if(p -> left) s.push(p->left);
            if(p -> right) s.push(p->right);
            ret.push_back(p ->val);
        }
        reverse(ret.begin(),ret.end());
        return ret;
    }
};

4.1 层序遍历

//广度优先搜索
void lvOrder(TreeNode *root,void (*visit)(TreeNode *p)){
	if(p == nullptr) return;
	queue<TreeNode*> node;
	node.push(root);
	while(!p.empty()){
		TreeNode *tmp = node.front();
		node.pop();
		visit(tmp);
		if(tmp -> left) node.push(tmp -> left);
		if(tmp -> right) node.push(tmp -> right);
	}
}

3.2 二叉树路径问题

3.2.1 二叉树高度

一目了然!

int getTreeHeight(TreeNode* root){
	//递归结束条件root == nullptr
	if(root == nullptr) return 0;
	int left = getTreeHeight(root -> left);//获取左子树高度
	int right = getTreeHeight(root -> right);//获取右子树高度
	return max(left,right) + 1;
}

3.2.2 节点路径

bool searchPath(TreeNode* root,TreeNode *target,vector<TreeNode*> &path){
	path.push_back(root);
	if(root == target){		
		return true;
	}
	bool found = false;
	if(root -> left) 
		found = searchPath(root -> left,target,path);//寻找左节点
	if(!found && root -> right)//没找到
		found = searchPath(root -> right,target,path);//寻找右节点
	if(!found)//如果没找到,删除节点
		path.pop_back();
	return found;
}

3.2.3 二叉树和为某值的路径

class Solution {
public:
    vector<vector<int>> pathSum(TreeNode* root, int sum) {
        if(root == nullptr) return get;
        vector<int> vec;
        getPath(root,sum,0,vec);
        return get;
    }
    void getPath(TreeNode *root,int sum,int curr, vector<int>& path){
        curr += root -> val;
        path.push_back(root -> val);
        bool isLeaf = root -> left == nullptr && root -> right == nullptr;//判断是叶子节点
        if(curr == sum && isLeaf) path.push_back(res);
        if(root -> left) getPath(root -> left,sum,curr,res);
        if(root -> right) getPath(root -> right,sum,curr,res);
        path.pop_back();
    }
    vector<vector<int>>get;
};

3.3 公共祖先问题

3.3.1 二叉搜索树两节点的公共祖先

TreeNode Get(TreeNode *root,TreeNode *p,TreeNode *q){
	if(root == nullptr) return nullptr;
	if(p -> val < root -> val && q -> val > root -> val
	|| p -> val > root -> val && q -> val < root -> val
	|| q == root
	|| p == root ) 
	return root;
	//否则
	if(p -> val < root -> val && q -> val < root -> val)//都在左边
		return Get(root -> left,p,q);
	else//都在右边
		return Get(root -> right,p,q);
}

3.3.2 平凡二叉树两节点公共祖先

基本思想:get两个节点路径,找到第最后一个相同的节点。如果

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == nullptr) return nullptr;
        vector<TreeNode*> res1,res2;
        searchPath(root,p,res1);
        searchPath(root,q,res2);
        int i;
        for(i = 0;i < res1.size() && i < res2.size() && res1[i] == res2[i];++i);
        return res1[i-1];
    }
    bool searchPath(TreeNode* root,TreeNode *target,vector<TreeNode*> &res){
        res.push_back(root);
        if(root == target) {
            return true;
            }
        bool found = false;
        if(!found && root -> left) found = searchPath(root-> left,target,res);
        if(!found && root -> right) found = searchPath(root -> right,target,res);
        if(!found)
            res.pop_back();
        return found;
    }
};

3.4 二叉树恢复问题

3.4.1 中序 + 先序

class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if(preorder.empty()) return nullptr;
        int base = preorder[0];
        TreeNode* root = new TreeNode(base);
        //划分左右子树,分治
        vector<int> lp,li;//左,先序、中序
        vector<int> rp,ri;//右,先序、中序
        int i = 0;
        for(;i < preorder.size() && inorder[i] != base;++i){
            lp.push_back(preorder[i+1]);
            li.push_back(inorder[i]);
        }
        ++i;
       for(;i < preorder.size();++i){
           rp.push_back(preorder[i]);
           ri.push_back(inorder[i]);
       }
       root -> left = buildTree(lp,li);
       root -> right = buildTree(rp,ri);
       return root;
    }
};

3.4.2 中序 + 后序

比较花费空间,但是比较好理解

class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if(postorder.empty()) return nullptr;
        int base = postorder.back();
        TreeNode* root = new TreeNode(base);
        //划分左右子树,分治
        vector<int> lp,li;//左,后序、中序
        vector<int> rp,ri;//右,后序、中序
        int i = 0;
        for(;i < inorder.size() && inorder[i] != base;++i){
            lp.push_back(postorder[i]);
            li.push_back(inorder[i]);
        }
        ++i;
       for(;i < inorder.size();++i){
           rp.push_back(postorder[i-1]);
           ri.push_back(inorder[i]);
       }
       
       root -> left = buildTree(li,lp);
       root -> right = buildTree(ri,rp);
       return root;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值