Leetcode22——括号生成(回溯法)


1. 写在前面

回溯[sù]:意思是逆着水流的方向走、逆水而行,逆流而上1。我一直读成[shuò]。
框[kuàng]架

1.建筑工程中,由梁、柱等联结而成的结构:完成主体~工程。
2.比喻事物的基本组织、结构:这部长篇小说已经有了一个大致的~。

 得,语文没学好。

2. 回溯法基本思想

 在许多递归问题当中,我们采取的方法都是穷尽所有的可能,从而找出合法的解。但是在某些情况下,当递归到某一层的时候,根据设置的判断条件,可以 judge 此解是不合法的。在这种情况下,我们就没必要再进行深层次的递归,从而可以提高算法效率。这一类算法我们称为“回溯法”,设置的判断条件称为“剪枝函数”。2
 个人觉得啊,回溯法可以类比为一棵自上而下的树,每一次满足题目要求的选择就是一个节点。当满足题目结束条件时,根到当前叶子节点的路径就是一个可行解。

3. 回溯法基本框架

 回溯法框架3

1、路径:也就是已经做出的选择。
2、选择列表:也就是你当前可以做的选择。
3、结束条件:也就是到达决策树底层,无法再做选择的条件。
代码方面,回溯算法的框架:
result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return

for 选择 in 选择列表:
    做选择
    backtrack(路径, 选择列表)
    撤销选择

其核心就是 for 循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」,特别简单。

4. 题目描述

  1. 括号生成
    数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
    示例:
    输入:n = 3
    输出:[
    “((()))”,
    “(()())”,
    “(())()”,
    “()(())”,
    “()()()”
    ]

5. 代码

 参考回溯法框架三点的解题。

  • 1.路径:也就是已经做出的选择。 ->这里使用StringBuilder保存已走路径,方便到时候回溯 — stringBuilder.deleteCharAt(X);
  • 2.选择列表:也就是你当前可以做的选择。 ->每次都可以选择 左括号 或者 右括号,但有限制条件:1.左右括号数量均不能超过n。2.当前StringBuilder的右括号数量必须小于左括号数量
  • 3.结束条件:也就是到达决策树底层,无法再做选择的条件。->StringBuilder长度为2*n。
	List<String> result = new ArrayList<String>();

    public List<String> generateParenthesis(int n) {
        //1、路径:也就是已经做出的选择。
        StringBuilder stringBuilder = new StringBuilder();

        //本题有两个需要注意:
        // 1.左括号和右括号数目均不能超过n
        // 2.如何判断括号合法?————————————————————当前已保存的括号数关系需满足:right括号数 <= left括号数。


        backTrack(n, 0, 0, stringBuilder);
        return result;
    }

 	/**
     * 回溯计算
     *
     * @param num           括号对数
     * @param leftNum       已有左括号数量
     * @param rightNum      已有右括号数量
     * @param stringBuilder 已保存的路径
     */
    private void backTrack(int num, int leftNum, int rightNum, StringBuilder stringBuilder) {

        //结束条件
        if (stringBuilder.length() >= num * 2) {
            result.add(stringBuilder.toString());
            return;
        }

        //做选择
        if (leftNum < num) {
            stringBuilder.append("(");
            backTrack(num, ++leftNum, rightNum, stringBuilder);
            //撤销选择
            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
            --leftNum;
        }

        //咱再做一次选择
        if (rightNum < leftNum) {
            stringBuilder.append(")");
            backTrack(num, leftNum, ++rightNum, stringBuilder);
            //撤销选择
            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
            --rightNum;
        }
    }

6. 总结

 一开始做题用的是暴力递归法,当括号总数达到2*n时才判断括号是否有效"((((((",看了题解之后才知道回溯法。回溯法题目做的还是不多,得多刷几题找找感觉。加油!

 	/**
     * 判断括号是否有效
     * @param array char数组
     * @return 是否有效
     */
    private boolean parenthesIsValid(char[] array) {
        int balance = 0;

        for (char c : array) {
            if ('(' == c) {
                balance++;
            } else {
                balance--;
            }

            if (balance < 0) {
                return false;
            }
        }

        return balance == 0;

    }

完~


  1. 百度百科. ↩︎

  2. [算法笔记] 回溯法总结. ↩︎

  3. Leetcode刷题java之39. 组合总和(回溯法以及回溯法框架). ↩︎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值