leetcode练习--递归问题

1. 17题

Given a digit string, return all possible letter combinations that the number could represent.

A mapping of digit to letters (just like on the telephone buttons) is given below.


给你这么一堆数字与字母的对应关系,然后给你一段数字组成的字符串,求可能的所有的字母字符串。

Input:Digit string "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

input:string

output:vector<string>

第一反应是利用递归,先算n-1长度的数字的转义+最后一位转义,但是写着写着就懵逼了,因为没办法控制返回值,而且需要全局变量控制vector<string>的长度,最后没有用递归也做出来了。看了一下讨论区的答案,感觉有一篇递归做得很好,拿来学习一下。

class Solution {
public:
    vector<string> letterCombinations(string digits) 
    {
        vector<string> res;
        if(digits.size()==0) return res;
        string local;
        vector<vector<char>> table(2,vector<char>());
        table.push_back(vector<char>{'a','b','c'}); // index 2
        table.push_back(vector<char>{'d','e','f'}); // 3
        table.push_back(vector<char>{'g','h','i'});
        table.push_back(vector<char>{'j','k','l'}); // 5
        table.push_back(vector<char>{'m','n','o'});
        table.push_back(vector<char>{'p','q','r','s'}); // 7
        table.push_back(vector<char>{'t','u','v'});
        table.push_back(vector<char>{'w','x','y','z'}); // 9
        
        backtracking(table,res,local,0,digits);
        return res;
    }
    
    void backtracking(const vector<vector<char>>& table, vector<string>& res, string& local, int index, const string& digits) {
        if(index==digits.size())
            res.push_back(local);
        else
            for(int i=0;i<table[digits[index]-'0'].size();i++) {
                local.push_back(table[digits[index]-'0'][i]);
                backtracking(table, res, local, index+1, digits);
                local.pop_back();
            }
    }
};

全局变量基本以参数的形式进行传递,通过在递归中加入循环使vector的长度以乘法的速度增长。

string &local local.push_back()和local.pop()控制可变字符串的长度,作为参数继续传递。

==============================================

==============================================

2. 中序遍历的树

给你n个数1234......n,这是一颗二叉树中序遍历的结果,让你求这些树是什么

For example,
Given n = 3, your program should return all 5 unique BST's shown below.

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3

input:n

output:vector<TreeNode*> res

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
using namespace std;
 struct TreeNode {
     int val;
     TreeNode *left;
     TreeNode *right;
     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 };

class Solution {
public:
	TreeNode *clone(TreeNode* s)
	{
		if (s == nullptr)
			return nullptr;
		TreeNode *c = new TreeNode(s->val);
		c->left = clone(s->left);
		c->right = clone(s->right);
		return c;
	}
	vector<TreeNode*> generateTrees(int n) {
		vector<TreeNode*> res;
		if (n == 0) return res;
		if (n == 1)
		{
			res.push_back(new TreeNode(1));
			return res;
		}
		Recursive(res, n - 1, new TreeNode(n));
		return res;
	}
	void Recursive(vector<TreeNode*> &res,int n,TreeNode* local)
	{
		if (n == 1)
		{
			TreeNode *last = new TreeNode(1);
			TreeNode *tree_n = clone(local);
			TreeNode *local_l = tree_n;
			while (local_l->left != nullptr)
			{
				//进行操作的树为tree_n local_l指向的是tree_n的节点 记录当前local_l的状态 
				//如果存在左子树local_lt则 local_l-左->lat  lat-右->local_lt  对tree_n状态进行更改之后clone出新树进行保存
				//local_l-左->local_lt 相当于把tree_n更新回来了。
				TreeNode* local_lt = local_l->left;
				TreeNode *lat = new TreeNode(n);
				local_l->left = lat;
				lat->right = local_lt;
				TreeNode* restore = clone(tree_n);
				res.push_back(restore);
				local_l->left = local_lt;
				local_l = local_l->left;
			}
			local_l->left = last;
			res.push_back(tree_n);
			TreeNode *tree_n_2 = clone(local);
			TreeNode *last_2 = new TreeNode(1);
			last_2->right = tree_n_2;
			res.push_back(last_2);
		}
		else
		{
			TreeNode *last = new TreeNode(n);
			TreeNode *tree_n = clone(local);
			TreeNode *local_l = tree_n;
			while (local_l->left != nullptr)
			{	
				TreeNode* local_lt = local_l->left;
				TreeNode *lat = new TreeNode(n);
				local_l->left = lat;
				lat->right = local_lt;
				TreeNode* restore = clone(tree_n);
				Recursive(res, n - 1, restore);
				local_l->left = local_lt;
				local_l = local_l->left;
			}
			local_l->left = last;
			Recursive(res, n - 1, tree_n);
			TreeNode *tree_n_2 = clone(local);
			TreeNode *last_2 = new TreeNode(n);
			last_2->right = tree_n_2;
			Recursive(res, n - 1, last_2);
		}
	}
	
	vector<int> inorderTraversal(TreeNode* root) {
		vector<int> res;
		backtrace(root, res);
		return res;
	}
	void backtrace(TreeNode *node, vector<int>& res)
	{
		if (node == NULL) return;
		if (node->left)
		{
			backtrace(node->left, res);
		}
		res.push_back(node->val);
		backtrace(node->right, res);
	}
};
int main()
{
	Solution s;
	vector<TreeNode*> m = s.generateTrees(5);
	for (int i = 0; i < m.size(); i++)
	{
		vector<int> prt = s.inorderTraversal(m[i]);
		for (int j = 0; j < prt.size(); j++)
		{
			cout << prt[j] << " ";
		}cout << endl;
	}
}

这题的难点不在递归,递归的规律很好解释,如果是从大到小插入节点比如:n到1

我们最后要得到一颗n节点的树称为tree(n)

对于tree(n)=tree(n-1)+(1个节点)

那么可以是这(一个节点)的右孩子

或者是 对于tree(n-1)每一个左孩子 tree(左孩子) 把(一个节点)插入到tree(左孩子)的位置,然后把tree(左孩子)加入到(1个节点)的右孩子处


难点主要在于指针的运算,由于在递归过程中tree是会各种变化的,因此引入clone函数使tree每次都相当于一个临时变量,且跟之前的tree仅仅是值相同,地址完全不同。


===================================================

===================================================

3.括号匹配

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

For example, given n = 3, a solution set is:

[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]

给你一个数n,然后输出所有合理的匹配括号(即:每个左括号都有右括号匹配)

看起来思路挺多的,其实括号匹配的规律是一共有n个左括号n个右括号,从开始到最后数括号的个数,在每一个位置上,左括号的个数>=右括号的个数

可以选择设置一个函数每次插入括号时判断一下,缺点就是每次都需要遍历你已经写过的括号。能不能每次插入时,括号的个数就是已知的呢?那么这里可以引入递归。

class Solution {
public:
	vector<string> generateParenthesis(int n) {
		vector<string> res; 
		string s = "(";
		Re(1, 1, 0, n, res, s);
		return res;
	}
	void Re(int len, int a, int b, int n,vector<string>& res,string &local)
	{
		if (len == 2*n-1)
		{
			local.push_back(')');
			res.push_back(local);
			local.pop_back();
			return;
		}
		if (a + 1 <= n)
		{
			local.push_back('(');
			Re(len + 1, a + 1, b, n, res, local);
			local.pop_back();
		}
		if (b + 1 <= a)
		{
			local.push_back(')');
			Re(len + 1, a, b + 1, n, res, local);
			local.pop_back();
		}
	}
};

ab分别是左右括号的个数,len为当前字符串长度

开始肯定是要插入一个左括号的,且最后插入的是右括号,判断好即可。


====================================================

====================================================

Given a set of candidate numbers (C(without duplicates) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

The same repeated number may be chosen from C unlimited number of times.

Note:

  • All numbers (including target) will be positive integers.
  • The solution set must not contain duplicate combinations.

For example, given candidate set [2, 3, 6, 7] and target 7
A solution set is: 

[
  [7],
  [2, 2, 3]
]

这个题还是想了相当一会儿的,用时大概75min,runtime19ms,大概思路还是递归,首先对原集合排序,

然后对于set(0,n) 表示从第0个数道第n(=size()-1)个数的集合。

比如例子【2,3,6,7】 target=14 根据用没用最后一个数

可以分成 【2,3,6】 target=14 和 【2,3,6,7】 target=7

终止的条件即target=0 或者 对只剩下最后一个元素的判断。

class Solution {
public:
	 vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
		 vector<vector<int>> re;
		 vector<int> res;
		 sort(candidates.begin(), candidates.end());
		 bct(candidates,candidates.size() - 1, target, res, re);
		 return re;
	 }
	 void bct(vector<int> nums, int must,int target,vector<int> &res,vector<vector<int>> &re)//nums[must] must in it;
	 {
		 if (0 == must)
		 {
			 if (target%nums[0] == 0)
			 {
				 for (int i = 0; i < target / nums[0]; i++)
					 res.push_back(nums[0]);
				 re.push_back(res);
				 for (int i = 0; i < target / nums[0]; i++)
					 res.pop_back();
				 return;
			 }
			 else
			 {
				 return ;
			 }
		 }
		 else
		 {
			 if (target - nums[must] >= 0)
			 {
				 res.push_back(nums[must]);
				 target -= nums[must];
				 if (target == 0)
				 {
					 re.push_back(res);
				 }
				 else
				 {					 
					 bct(nums, must, target, res, re);
				 }
				 res.pop_back();
				 bct(nums, must - 1, target+nums[must], res, re);
			 }
			 else bct(nums,must-1, target, res, re);
			 return;
		 }
	 }
};

比较麻烦的地方还是pop,push,脑子得清楚。。。想明白递归逻辑之后,这个题就很简单了。


后面还一道差不多的题,我把这个代码改了一点点交上去了,runtime是13ms,用时9min,难度不如上面那题。

Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

Each number in C may only be used once in the combination.

Note:

  • All numbers (including target) will be positive integers.
  • The solution set must not contain duplicate combinations.

For example, given candidate set [10, 1, 2, 7, 6, 1, 5] and target 8
A solution set is: 

[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]
  大概意思就是不让重复用数了,其余同上。
class Solution {
public:
	 vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
		 vector<vector<int>> re;
		 vector<int> res;
		 sort(candidates.begin(), candidates.end());
		 bct(candidates,candidates.size() - 1, target, res, re);
		 return re;
	 }
	 void bct(vector<int> nums, int must,int target,vector<int> &res,vector<vector<int>> &re)//nums[must] must in it;
	 {
		 if (0 == must)
		 {
			 if (target-nums[0] == 0)
			 {
				 res.push_back(nums[0]);
				 re.push_back(res);
				 res.pop_back();
				 return;
			 }
			 else
			 {
				 return ;
			 }
		 }
		 else
		 {
			 if (target - nums[must] >= 0)
			 {
				 res.push_back(nums[must]);
				 target -= nums[must];
				 if (target == 0)
				 {
					 re.push_back(res);
				 }
				 else
				 {					 
					 bct(nums, must-1, target, res, re);
				 }
				 res.pop_back();
				 while (must>0&&nums[must] == nums[must - 1]) must--;
				 if (must>0)
					bct(nums, must - 1, target+nums[must], res, re);
			 }
			 else
			 {
				 while (must>0&&nums[must] == nums[must - 1]) must--;
				 if(must>0)
					bct(nums, must - 1, target, res, re);
			 }
			 return;
		 }
	 }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值