每日算法 - 括号生成问题

目录

题目

解题思路

暴力法

暴力法代码

回溯法

回溯法代码


题目

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

 

示例:

输入:n = 3
输出:[
       "((()))",
       "(()())",
       "(())()",
       "()(())",
       "()()()"
     ]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/generate-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


解题思路


暴力法

思想:将所有可能性列举出来,然后逐个检查每个可能性是否有效。针对这个问题的具体就是:输入n(n对括号),可以生成2^(2n)个由 “ ( ” 和 " ) " 字符构成的序列,然后逐个检查是否有效;

  • 生成所有序列;
  • 逐个检查有效性;
  • 模式识别:子问题和原问题具有相同结构。考虑自上而下的递归

1)生成序列实现方式:

因为长度为n的序列就是在长度为n-1序列的基础上再加一个 “ ( ” 或者 " ) ",所以考虑使用递归。递归解决结构相同的子问题。

2)检查序列是否有效方式:

遍历这个序列,使用变量 balance 表示左括号的数量减去右括号的数量。如果遍历过程中 balance 的值小于0,或者结束时候 balance 的值不为0,那么该序列就是无效的,否则他就是有效的。


暴力法代码

1、生成序列递归函数:

  • 首先定义递归头:如果当前序列有效,则将其加入有效结果集合中;否则执行递归体,继续生成序列;
  • 然后书写递归体:递归生成序列,假设当前序列加入一个左括号或者右括号,然后调用递归函数继续解决子问题;

2、有效性检查函数:

  • balance  变量随着左括号和右括号的数量而更新。

      

import java.util.ArrayList;
import java.util.List;

/**
 * 括号生成问题
 * @author tyler
 *
 */
public class Solution{
	
	public List generateParenthesis(int n) {
		List combinations = new ArrayList<>();
		generateALL(new char[2*n], 0, combinations);
		return combinations;
	}
	 
	// 递归生成序列
	public void generateALL(char[] current, int pos, List result) {
		// 递归头
		 if (pos == current.length) {
			if (valid(current)) {
				// 当前序列有效则将其加入结果集合中
				result.add(new String(current));
			}
		}else {
			// 递归体,在子问题的基础上,做相同的操作
			current[pos] = '(';
			generateALL(current, pos+1, result);
			current[pos] = ')';
			generateALL(current, pos+1, result);

		}
	}
	 
	// 检查有效性
	public boolean valid(char[] current) {
		int balance = 0;
		for (char c : current) {
			if (c == '(') {
				balance++;
			}else {
				balance--;
			}
			if (balance < 0) {
				return false;
			}
		}
		return (balance == 0);
	}
	 
}

复杂度分析:

时间复杂度:O(2^{2n}n),对于 2^{2n}个序列中的每一个,我们用于建立和验证该序列的复杂度为 O(n)。

空间复杂度:O(n),除了答案数组之外,我们所需要的空间取决于递归栈的深度,每一层递归函数需要 O(1) 的空间,最多递归 2n 层,因此空间复杂度为 O(n)。


回溯法

思想:在暴力法的基础上,增加生成子问题的条件,减少列举的可能性数目。

在这儿的具体是:暴力法还有改进的余地:我们可以只在序列仍然保持有效时才添加 '(' or ')',而不是像 方法一 那样每次添加(增加添加序列的条件,减少列举的可能性数目)。我们可以通过跟踪到目前为止放置的左括号和右括号的数目来做到这一点。

  • 关键字:有效序列;
  • 模式识别:确保每一步产生有效的序列,利用回溯搜索其他可能的解。

如果左括号数量不大于 n,我们可以放一个左括号。如果右括号数量小于左括号的数量,我们可以放一个右括号。


回溯法代码

回溯法思想:参考之前的文章:https://blog.csdn.net/Longtermevolution/article/details/105395558

当前位置有两种选择,为了把两种选择都进行探索,所以在探索了当前位置加入 '(' 的解空间之后,需要reset当前位置,再去探索当前位置加入 ')' 的解空间。

import java.util.ArrayList;
import java.util.List;

/**
 * 括号生成问题,回溯法
 * @author tyler
 *
 */
public class Solution{
	
	public List generateParenthesis(int n) {
        List ans = new ArrayList();
        backtrack(ans, new StringBuilder(), 0, 0, n);
        return ans;
    }

    public void backtrack(List ans, StringBuilder cur, int open, int close, int max){
        if (cur.length() == max * 2) {
            ans.add(cur.toString());
            return;
        }

        if (open < max) {
            cur.append('(');
            backtrack(ans, cur, open+1, close, max); // 探索当前位置加入左括号的解空间
            cur.deleteCharAt(cur.length() - 1);// 但是当前位置有可能加入右括号,所以需要reset当前位置。
        }
        if (close < open) {
            cur.append(')');
            backtrack(ans, cur, open, close+1, max);// 同理
            cur.deleteCharAt(cur.length() - 1);
        }
    }

}

每日算法 - 括号生成问题_第1张图片

你可能感兴趣的:(面试算法题,每日算法系列)