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个左括号还没用到,有0个右括号需要匹配。而临界状态,就是当左括号全部用完并且右括号全部匹配完毕时,将字符串存进vector中。稍微难一点的是递归的过程,但其实也不难。我们可以这样想,当左括号还没用完时,我们可以尝试往字符串中加一个左括号,这样的话左括号还没用到的个数减1,右括号需要匹配的个数加1。同样,当右括号还没全部匹配时,自然地,我们往字符串中加一个右括号,这样右括号需要匹配的个数减1,而左括号则保持不变。这样一次次递归,当二者全部为0时,全部情况考虑完毕,这样问题就顺利解决了。
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> v;
parenthesis(v, "", n, 0);
return v;
}
void parenthesis(vector<string>& v, string str, int left, int right) {
if (left == 0 && right == 0) {
v.push_back(str);
return;
}
if (right > 0) {
parenthesis(v, str + ")", left, right - 1);
}
if (left > 0) {
parenthesis(v, str + "(", left - 1, right + 1);
}
}
};
Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers.
Example 1:
Input: k = 3, n = 7
Output: [[1,2,4]]Example 2:
Input: k = 3, n = 9
Output: [[1,2,6], [1,3,5], [2,3,4]]
这道题也是很经典的运用回溯法解决的例子。正如上面所提到的,关键是要找到三个关键点:初始状态、临界状态和迭代过程。我们通过这道题分别对这三个点进行简要地分析。
这道题要求我们输出所有的k个不同的数之和为n的情况。首先我们来考虑初始状态,很显然,在刚开始的时候,第一个数我们肯定是先从1开始,然后第二个数在第一个数的基础上加1,这样直到找到的k个数之和为n。因此很明显,初始情况下,k个数需要匹配,匹配的总和为n,从1开始进行匹配。临界情况也很明显,当我们匹配了k个数且这k个数的总和为n,则说明此种匹配情况成立,将这种情况存进vector中即可。最后再来看看迭代的过程,当我们选定第一个数i之后,那么就只需要匹配后面的(k-1)个数即可,匹配的总和为(n-i),由于不能重复,因此第二个数将从(i+1)开始匹配。这样一个个数下去,找得到符合要求的数字j就存进vector中,否则就返回,将该数pop掉,将j+1存进数组中重新考虑情况,直到数字9为止。
class Solution {
public:
vector<vector<int>> combinationSum3(int k, int n) {
vector<vector<int>> v;
vector<int> vec;
combination(v, vec, k, n, 1);
return v;
}
void combination(vector<vector<int>>& v, vector<int>& vec, int k, int n, int start) {
if (vec.size() == k && n == 0) {
v.push_back(vec);
return;
}
for (int i = start; i <= 9; i++) {
vec.push_back(i);
combination(v, vec, k, n - i, i + 1);
vec.pop_back();
}
}
};
以上是我对这两道回溯问题的一些想法,有问题还请在评论区讨论留言~