接到一个同学的询问,说是在做一个括号匹配的输出,大意是这样的:
定义一个整数n 表示括号对数,然后枚举出所有的合理括号组合形式。如当n = 1,只有“()”这样一种情形;当n = 2时,有“()()”和“(())”两种情况。依次列举。
由于我使用Java还不大习惯,我使用的是C++实现。而我的同学则使用Java的实现。现将两种算法共享如下:
㈠:Java的算法:
个人评价:算法简洁,不过由于不是自己写的,现在仍感觉有点难看懂。
- public class Kuohao
- {
- int n;
- String All="";//若此处改为String All = null;则会输出结果前端都会有“null”字符。
- public Kuohao(int n)
- {
- this.n=n;
- }
- public void PrintAll(String All,int left,int right)
- {
- if(right==0)
- {
- System.out.println(All);
- return;
- }
- else if(left==right)
- {
- All+="(";
- PrintAll(All,--left,right);
- }
- else if(left<right && left>0)
- {
- String a=All;
- All+="(";
- PrintAll(All,--left,right);
- left++;
- a+=")";
- PrintAll(a,left,--right);
- }
- else if(left==0)
- {
- All+=")";
- PrintAll(All,left,--right);
- }
- }
- public static void main(String []args)
- {
- Kuohao kuohao=new Kuohao(3);
- kuohao.PrintAll(kuohao.All,kuohao.n,kuohao.n);
- }
- }
输出结果:
- ((()))
- (()())
- (())()
- ()(())
- ()()()
㈡:自己写的C++算法:
个人评价:应该是简单易懂的,但是代码长度比起Java来显然长了许多。
- #include <iostream>
- #include <string>
- using namespace std;
- void main(){
- void PaintAll(string All,int left,int right);
- string All = "";
- int n = 3;
- PaintAll(All,n,n);
- }
- void PaintAll(string All,int left,int right){
- string LeftAll = All,RightAll = All;//由此以下三句为保护All与left,right.
- int LeftLeft = left,LeftRight = right;//缘由是因为用了for循环,在循环中的两种情况下,使用的都
- //必须是同一个数据源,不可以被更改
- int RightLeft = left,RightRight = right;
- if(left == 0){
- while(right != 0){
- All += ")";
- right --;
- }
- cout << All << endl;
- return ;
- }
- for(int i = 0;i < 2; i ++){
- switch(i){
- case 0:
- LeftLeft --;
- if(LeftLeft < 0){
- LeftLeft = 0;
- continue;
- }else{
- LeftAll += "(";
- PaintAll(LeftAll,LeftLeft,LeftRight);
- }
- break;
- default:
- if(RightLeft < RightRight){
- RightRight --;
- if(RightRight < 0){
- RightRight = 0;
- break;
- }else{
- RightAll += ")";
- PaintAll(RightAll,RightLeft,RightRight);
- }
- }
- break;
- }
- }
- }
输出结果:
若没有Line 13,14,16的保护,则输出的错误结果
- ((()))
- ((()))
- (()())
- (()())
- ()(())
- ()(())
- ()()()
- ()()()
很奇怪的结果吧,这是因为当for循环中i = 1;时所使用的All,left,right都已经是在i = 0时所加工过的了,不是原始数据了。
佐证:
看第4,5,6,7行,这就是问题所在。
C++算法的优化:
可以看到,for循环里,变量i只是从0变化到1,switch语句也只是进行0,与1的选择,所以可以省去for与switch语句。又有进行递归时,传进递归函数的必须是初始的原始数据,所以不必设定两组暂时的存储变量{LeftAll,LeftLeft,LeftRight},{RightAll,RightLeft,RightRight},而可以改为一组{tempAll,tempLeft,tempRight},在每个递归函数前,重新对tempLeft和tempRight赋值为:
tempAll = All;
tempLeft = Left;
tempRight = Right;
改进的算法如下,可以看见代码的长度也缩减为44行了,如果去除包含3句头语句,则比Java算法代码还少一句,所以可见优化的作用:
- #include <iostream>
- #include <string>
- using namespace std;
- void main(){
- void PaintAll(string All,int left,int right);
- string All = "";
- int n = 3;
- PaintAll(All,n,n);
- }
- void PaintAll(string All,int left,int right){
- if(left == 0){
- while(right != 0){
- All += ")";
- right --;
- }
- cout << All << endl;
- return ;
- }
- string tempAll = All;
- int tempLeft = left,tempRight = right;
- tempLeft --;
- if(tempLeft < 0){
- tempLeft = 0;
- }else{
- tempAll += "(";
- PaintAll(tempAll,tempLeft,tempRight);
- }
- tempAll = All;
- tempLeft = left;
- tempRight = right;
- if(tempLeft < tempRight){
- tempRight --;
- if(tempRight < 0){
- tempRight = 0;
- }else{
- tempAll += ")";
- PaintAll(tempAll,tempLeft,tempRight);
- } //else if
- } //if
- }