您的位置:首页 > 编程语言 > C语言/C++

LeetCode c语言-Generate Parentheses和买票找零问题

2017-09-18 17:35 591 查看
Title:

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:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]


这道题目难度颇高,如果没有正确的思路出发点,是很难做出来的。

典型的递归+栈问题。一般碰到要输出很多种结果的问题,递归是第一考虑的思路。

对于这道题,符合要求的序列一定满足以下条件:

从左边开始数,右括号的个数一定不可能大于左括号,否则会出现))这种情况。

而且第一个一定是左括号。

代码如下:

solution:

/**
* Return an array of size *returnSize.
* Note: The returned array must be malloced, assume caller calls free().
*/

void generate(char** result, int* Size, int l, int r, char* tmp, int index) {
if(l==0 && r==0) {
tmp[index]=0;
result[*Size]=(char*)malloc(sizeof(char)*index);
strcpy(result[*Size],tmp);
(*Size)++;
return;
}
if (l>0) {
tmp[index]='(';
generate(result, Size, l-1, r, tmp, index+1);
}
if(r>0 && l<r) {
tmp[index]=')';
generate(result, Size, l, r-1, tmp, index+1);
}
}
char** generateParenthesis(int n, int* returnSize) {
char** result=(char**)malloc(sizeof(char)*1000000);
char* tmp=(char*)malloc(sizeof(char)*(n*2+1));
int l=n,r=n; /* l和r分别表示左括号和右括号的剩余个数 */

*returnSize=0;
generate(result,returnSize,l,r,tmp,0);
return result;
}


解析递归函数:

l和r表示剩余的左括号和右括号。

那么当l>0时,可以插入左括号,记住左括号是只要有就可以插入的。然后进行递归,记住l-1,表示减少一个。r不变。

if (l>0) {
tmp[index]='(';
generate(result, Size, l-1, r, tmp, index+1);
}


当r>0时,还要注意,一定要l<r,也就是剩余的l小于r,那么已经插入的l肯定大于r,这个时候才可以插入右括号。因为要始终保证,左括号大于等于右括号。这个序列才有效。

if(r>0 && l<r) {
tmp[index]=')';
generate(result, Size, l, r-1, tmp, index+1);
}


当l==0 并且r==0,表明全部插入,这个时候说明这个序列是有效的,可以存起来并返回。

if(l==0 && r==0) {
tmp[index]=0;
result[*Size]=(char*)malloc(sizeof(char)*index);
strcpy(result[*Size],tmp);
(*Size)++;
return;
}


解析到这里,其实能够看出来,如果找到了左括号数量一定要大于右括号的隐含要求,再配合递归,其实非常简单。关键是能不能找到这个要求。

在《编程之美》一书中,也有类似的问题:买票找零

《编程之美》4.3买票找零:2n个人排队买票,其中n个人持50元,n个人持100元。每张票50元,且一人只买一张票。初始时售票处没有零钱找零。请问这2n个人一共有多少种排队顺序,不至于使售票处找不开钱?

这道题其实和括号匹配问题是一样的,50元的相当于左括号,100元相当于右括号,一定要左括号大于右括号才能成立。

而且这种类型的题目牵扯到一个公式:Catalan number

比如对这道找零的题目进行数学分析:

分析1:队伍的序号标为0,1,…,2n-1,并把50元看作左括号,100元看作右括号,合法序列即括号能完成配对的序列。对于一个合法的序列,第0个一定是左括号,它必然与某个右括号配对,记其位置为k。那么从1到k-1、k+1到2n-1也分别是两个合法序列。那么,k必然是奇数(1到k-1一共有偶数个),设k=2i+1。那么剩余括号的合法序列数为f(2i)*f(2n-2i-2)个。取i=0到n-1累加即: 

f(2n)=f(0)*f(2n-2)+f(2)*f(2n-4)+······+f(2n-4)*f(2)+f(2n-2)*f(0) 

并且令f(0)=1,再由组合数C(0,0)=0,可得 



以上就是一种卡特兰数的通项公式。

Catalan 的应用场景:

1.出栈次序(买票问题)

对于一个无限大的栈,一共n个元素,请问有几种合法的入栈出栈形式?

分析:n个元素出栈入栈操作共有2n个,且任何前x个操作中入栈操作必须不少于出栈操作,直接用f(2n)计算即可。

2.括号化问题(矩阵连乘问题)

P = a1 * a2 * a3 * … * an,其中ai是矩阵。根据乘法结合律,不改变矩阵的相互顺序,只用括号表示成对的乘积,试问一共有几种括号化方案?

分析:n个矩阵相乘需要n-1对括号,且任何前x个元素中”(”的个数一定大于”)”的个数,所以应该为f(2(n-1))

3.街区对角问题

某个城市的某个居民,每天他须要走过2n个街区去上班(他在其住所以北n个街区和以东n个街区处工作)。如果他不跨越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路? 



分析:其实也与括号问题相似,这个人一共要走2n步,其中向右与向上分别n步,那么,假设他第一步是向右的,那么之后的任何时刻,他向右的步数都不能少于想上的步数,即f(2n),当然,他也可以第一步时先向上,同样有f(2n)种走法,两者应该是以对角线对称的,所以答案为2*f(2n)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: