[18]括号生成和全排列

文章介绍了在LeetCode上的两道经典问题——括号生成和全排列,分别给出了使用回溯算法的解决方案。对于括号生成,通过递归和回溯生成所有可能的合法括号组合;全排列问题同样利用回溯,动态维护数组并避免重复。文章强调了在处理结果列表时,需注意深拷贝以防止后续操作影响已保存的结果。
摘要由CSDN通过智能技术生成

*内容来自leetcode

1.括号生成

题目要求

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例 1:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:

输入:digits = ""
输出:[]

思路

没有思路

这个回溯算法,本质上也是递归,官方给出了三种解答。

暴力法

长度为n的序列就是在n-1序列上加一个(或者)。

可以在加完之后判断是否有效,有效则加入结果列表

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        def trackBack(result):
            if len(result) == 2*n:
                if valid(result):
                    results.append("".join(result))
            else:
                result.append("(")
                trackBack(result)
                result.pop()
                result.append(")")
                trackBack(result)
                result.pop()
        def valid(strs):
            balance = 0
            for str in strs:
                if str == "(":
                    balance +=1
                else:
                    balance -=1
                if balance < 0:
                    return False
            return balance == 0
        
        results = list()
        trackBack([])
        return results

第二种即是对第一种算法的优化,可以在每次添加时判断左右括号数目关系,来判断添加何种字符,而不是最后进行判断,减少无用结果的生成

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        #对暴力法进行优化
        def trackBack(result,left,right):
            if len(result) == 2*n:
                results.append("".join(result))
            else:
                if left < n:
                    result.append("(")
                    trackBack(result,left+1,right)
                    result.pop()
                #当左括号多于右括号时才能添加右括号
                if left > right:
                    result.append(")")
                    trackBack(result,left,right+1)
                    result.pop()

        results = list()
        trackBack([],0,0)
        return results

第三种方法:

任何一个括号序列都一定是由 ‘(’开头,并且第一个 ‘(’一定有一个唯一与之对应的 ‘)’。这样一来,每一个括号序列可以用 (a)b 来表示,其中 a 与 b分别是一个合法的括号序列(可以为空)。

那么,要生成所有长度为 2n 的括号序列,我们定义一个函数 generate(n)来返回所有可能的括号序列。那么在函数 generate(n) 的过程中:

我们需要枚举与第一个 ‘(’的位置 2i+1;
递归调用 generate(i)即可计算 a 的所有可能性;
递归调用 generate(n−i−1) 即可计算 b 的所有可能性;
遍历 a 与 b的所有可能性并拼接,即可得到所有长度为 2n 的括号序列。
为了节省计算时间,我们在每次 generate(i)函数返回之前,把返回值存储起来,下次再调用 generate(i)时可以直接返回,不需要再递归计算。

贴上代码供学习

class Solution:
    @lru_cache(None)
    def generateParenthesis(self, n: int) -> List[str]:
        if n == 0:
            return ['']
        ans = []
        for c in range(n):
            for left in self.generateParenthesis(c):
                for right in self.generateParenthesis(n-1-c):
                    ans.append('({}){}'.format(left, right))
        return ans


#评论区提出的另一种实现
class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        if n == 1:
            return list({'()'})
        res = set()
        for i in self.generateParenthesis(n - 1):
            for j in range(len(i) + 2):
                res.add(i[0:j] + '()' + i[j:])
        return list(res)

2.全排列

题目要求

给定一个不含重复数字的数组 nums ,返回其所有可能的全排列 。可以 按任意顺序 返回答案。

示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:

输入:nums = [0,1]
输出:[[0,1],[1,0]]

思路

据说是一道很经典的回溯题。

记录一下我失败的代码

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        def trackBack(result,origin,stk):
            if len(result) == len(nums):
                results.append(result)
                        
            if origin:
                stk.append(origin.pop())
                trackBack(result,origin.copy(),stk.copy())
                result = []

            if stk:
                result.append(stk.pop())
                trackBack(result,origin.copy(),stk.copy())
                
        stk = []
        results = []
        trackBack([],nums.copy(),stk)
        return results

以及官方的代码

class Solution:
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        def backtrack(first = 0):
            # 所有数都填完了
            if first == n:  
                res.append(nums[:])
            for i in range(first, n):
                # 动态维护数组
                nums[first], nums[i] = nums[i], nums[first]
                # 继续递归填下一个数
                backtrack(first + 1)
                # 撤销操作
                nums[first], nums[i] = nums[i], nums[first]
        
        n = len(nums)
        res = []
        backtrack()
        return res

值得一提的是,在向最后的结果列表添加重排列过后的数组时,采用的是如下的语句

if first == n:  
    res.append(nums[:])

而不是

if first == n:  
    res.append(nums)

这是因为nums是数组的对象,也就是说添加后nums发生变化时,结果列表中的nums也会随之发生变化,到最后保存的结果都是一样的。而nums[:]是保存列表元素,也就是在列表当前值,在后续列表变化时不会影响到已经保存的。效果类似于copy()函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值