• 递归和回溯解决括号匹配问题


    递归和回溯解决括号匹配问题

    递归在之前的文章中有提及,有朋友在后台大呼不过瘾,刚好又刷到了一道可以用到递归的算法题,还是我们的老朋友:匹配有效括号,废话不多说,先上题目。

    数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
    
     
    
    示例:
    
    输入:n = 3
    输出:[
           "((()))",
           "(()())",
           "(())()",
           "()(())",
           "()()()"
         ]
    
    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/generate-parentheses
    

    经过分析,这涉及到了两个关键问题:

    1. 如何判断组合成的括号字符串是有效的
    2. 如何构建所有可能性的括号字符串。

    第一个问题比较容易想通,必须要左右括号成对出现即可。
    如果是暴力解法可以考虑把这两个问题分成两步去处理好,而如果是用递归,肯定是希望每次递归的时候不仅创建出排列组合,且这个组合出来的字符串是有效的。

    func generateParenthesis(n int) []string {
    	return generate(n)
    }
    
    
    func generate(n int) []string {
    	cache := map[int][]string{}
    
    	if cache[n] != nil {
    		return cache[n]
    	}
    	var ans []string
    
    	if n == 0 {
    		ans = append(ans, "")
    
    	} else {
    		for c := 0; c < n; c++ {
    			currentLeft := generate(c)
    			currentRight := generate(n - c -1)
    			for _, value_left := range currentLeft {
    				for _, value_right := range currentRight {
    					ans = append(ans, "(" + value_left + ")" + value_right)
    				}
    			}
    		}
    	}
    	cache[n] = ans
    	return ans
    }
    

    递归的关键在于找到边界,也就是分别对左括号和右括号的创建进行递归,这样就能生成出符合条件的字符串。总体来说这次递归的解法非常符合人性,条理清晰,也用到一个cache的map来存储结果,提高执行效率。

    其实这道题也可以使用回溯的方法来实现,具体代码实现如下:

    func generateParenthesis2(n int) []string {
    	ans := new([]string) // 设置一个指向类型为string的切片的指针
    
    	var curr []byte
    	backtrack(ans, curr, 0, 0, n)
    
    	return *ans // 反取指针,获得结果切片
    }
    
    // 回溯函数
    func backtrack(ans *[]string, curr []byte, open int, close int, max int) {
    	if len(curr) == max * 2 {
    		*ans = append(*ans, string(curr[:]))
    		return
    	}
    
    	if open < max { // 左括号没有超过最大值,则添加做括号,最后删除切片的最后一个元素
    		curr = append(curr, '(')
    		backtrack(ans, curr, open+1, close, max)
    		curr = append(curr[0:len(curr)-1], curr[len(curr):]...) // 这个删除方式最直接,但是
    	}
    
    	if close < open { // 当右括号比作括号少,则添加,最后删除切片的最后一个元素
    		curr = append(curr, ')')
    		backtrack(ans, curr, open, close+1, max)
    		curr = append(curr[0:len(curr)-1], curr[len(curr):]...)
    	}
    }
    

    回溯也就是动态规划,把每次结果都带入到下一次回溯中,通过比较开闭括号的个数,来动态地调整。
    算法题有时候可以考虑多种解法去实现,刷题的同时也能体会到不同编程语言的妙处。
    本文首发自我的微信公众号:成都有娃儿,欢迎关注。

  • 相关阅读:
    单元测试
    英语学习app案列分析
    基于GUI的四则运算
    个人作业1——四则运算题目生成程序(基于控制台)
    一种新体验———构建之法
    个人作业3——个人总结(Alpha阶段)
    单元测试
    英语学习APP案例分析
    结对编程(70,73)
    软件工程HW1-四则运算软件
  • 原文地址:https://www.cnblogs.com/freephp/p/13340436.html
Copyright © 2020-2023  润新知