leetcode_22_括号生成_中等_回溯

题目

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

在这里插入图片描述
回溯套路:
在这里插入图片描述
搜索算法:深度搜索树,列出所有情况,
每一层都有m种选择
思想是专门选择某个元素,列下一个,再固定,等固定该元素所所有情况都列完了,再返回上一层列举另一个元素。这样遍历整个树。

回溯:就是保存一些状态变量,状态变量满足某些情况下才去列举元素,否则跳过这个选择。这样情况就会少一些。 不是暴力的列出所有元素组合。

思路

暴力法

先枚举出所有情况,再筛选出满足的。
枚举出所有情况:
在这里插入图片描述

在这里插入图片描述
筛选出满足的:
在这里插入图片描述

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> combinations = new ArrayList();
        generateAll(new char[2 * n], 0, combinations);
        return combinations;
    }

    public void generateAll(char[] current, int pos, List<String> 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);
    }
}

在这里插入图片描述

回溯法

我们只在在序列仍然有效的时候才添加’(’ or ‘)’ ,而不是像暴力法不管前面的,列举所有情况。

方法:跟踪到目前为止放置的左括号和右括号的数目来做到这一点。记录前面的状态。

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

全排列也是一样,不是每个dfs都循环(i = 0; i < n; i++) ,而是当数字i used[i]访问过了,就不会再往下找了,固定前面的,枚举,既不会漏也不会重。

生成括号树,由于先生成(后生成)的() 和 先生成)后生成(的() 没有区别,所以减掉当left < right这一种情况。剪枝

在这里插入图片描述

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

public class leetcode_22_generateParenthesis_huisu_2 {

    public static List<String> generateParenthesis(int n) {
        // StringBuilder curStr
        // 终止条件 curStr.length == 2*n
        // 搜索 当满足条件时,添加括号,继续向下找
        // 满足条件: 当左括号数量小于n时,可以添加左括号
        // 当右括号数量小于左括号数量时,可以添加右括号

        if (n == 0) return new ArrayList<>();

        List<String> ans = new ArrayList<>();
        StringBuilder curStr = new StringBuilder();
        dfs(curStr, ans, 0, 0, n);
        return ans;
    }

    public static void dfs(StringBuilder curStr, List<String> ans,
                           int left, int right, int max) {
        if (curStr.length() == 2 * max) {
            ans.add(curStr.toString());
            return;
        }

        // 剪枝
        if(left < right) {
            return;
        }

        if(left < max) {
            curStr.append('(');
            dfs(curStr, ans, left + 1, right, max);
            curStr.deleteCharAt(curStr.length() - 1);
        }

        if(right < left) {
            curStr.append(')');
            dfs(curStr, ans, left, right + 1, max);
            curStr.deleteCharAt(curStr.length() - 1);
        }

    }

    public static void main(String[] args) {
        List<String> res = generateParenthesis(2);
        System.out.println(res);
    }
}

在这里插入图片描述

广度优先搜索

通过编写广度优先遍历的代码,读者可以体会一下,为什么搜索几乎都是用深度优先遍历(回溯算法)。

广度优先遍历,得程序员自己编写结点类,显示使用队列这个数据结构。深度优先遍历的时候,就可以直接使用系统栈,在递归方法执行完成的时候,系统栈顶就把我们所需要的状态信息直接弹出,而无须编写结点类和显示使用栈。

下面的代码,读者可以把 Queue 换成 Stack,提交以后,也可以得到 Accept。

读者可以通过比较:

1、广度优先遍历;

2、自己使用栈编写深度优先遍历;

3、使用系统栈的深度优先遍历(回溯算法)。

来理解 “回溯算法” 作为一种 “搜索算法” 的合理性。

还是上面的题解配图(1),使用广度优先遍历,结果集都在最后一层,即叶子结点处得到所有的结果集,编写代码如下。

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

public class Solution {

    class Node {
        /**
         * 当前得到的字符串
         */
        private String res;
        /**
         * 剩余左括号数量
         */
        private int left;
        /**
         * 剩余右括号数量
         */
        private int right;

        public Node(String str, int left, int right) {
            this.res = str;
            this.left = left;
            this.right = right;
        }
    }

    public List<String> generateParenthesis(int n) {
        List<String> res = new ArrayList<>();
        if (n == 0) {
            return res;
        }
        Queue<Node> queue = new LinkedList<>();
        queue.offer(new Node("", n, n));

        while (!queue.isEmpty()) {

            Node curNode = queue.poll();
            if (curNode.left == 0 && curNode.right == 0) {
                res.add(curNode.res);
            }
            if (curNode.left > 0) {
                queue.offer(new Node(curNode.res + "(", curNode.left - 1, curNode.right));
            }
            if (curNode.right > 0 && curNode.left < curNode.right) {
                queue.offer(new Node(curNode.res + ")", curNode.left, curNode.right - 1));
            }
        }
        return res;
    }
}

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/generate-parentheses/solution/hui-su-suan-fa-by-liweiwei1419/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值