LeetCode22 生成所有括號對
本文始發於個人公眾號:TechFlow,原創不易,求個關注
連結
Generate Parentheses
難度
Medium
描述
Given n pairs of parentheses, write a function to generate all combinations
of well-formed parentheses.
給定n對括號,要求返回所有這些括號組成的不同的合法的字串
For example, given n = 3, a solution set is:
[ "((()))", "(()())", "(())()", "()(())", "()()()" ]
題解
這道題目非常有意思,解法也很多,還是老規矩,我們先由易到難,先從最簡單的方法開始入手。
我們來簡單分析一下題目,n個括號對意味著字串的長度是2n,我們利用排列組合可以計算出,所有的組合種數一共有\(C_{2n}^n\)種。算一下會知道,這個數是很大的,也就是說我們哪怕一開始就知道答案,把答案遍歷一遍也會有很高的耗時,所以這道題對於時間複雜度的要求應該不會很高。
暴力
能想到最簡單的方法,當然是暴力,不要看不起這個樸素的演算法,很多時候靈感都是從暴力當中獲取的。但是這道題暴力不太容易寫,因為會有一種無從入手的感覺,我們知道要暴力,但是並不知道應該怎樣暴力。這道題不存在可以直接列舉的樸素元素,必須要我們拐個彎才行。
怎麼拐彎呢,其實答案我剛才已經說出來了。n個括號對,也就是說一共2n個字元,我們可以列舉n個'('分別放在什麼位置,剩下的自然就是')'了。看起來很有道理,但是有一個問題,就是這個思路並沒有辦法通過迴圈直接實現。這其實已經進化成了一個搜尋問題了,我們要搜尋所有可以擺放括號的可能性。
如果你能從暴力方法跳躍到搜尋問題,那麼說明你離寫出程式碼已經很接近了。如果不行,那麼我建議你花點時間去學習一下搜尋演算法專題。
對於搜尋問題而言,這已經很簡單了,我們搜尋的空間是明確的,2n個位置,搜尋的內容,對於每個位置我們可以擺放'('也可以擺放')'。那麼程式碼自然而然呼之欲出:
def dfs(pos, left, right, n, ret, cur_str): """ pos: 當前列舉的位置 left: 已經放置的左括號的數量 right: 已經放置的右括號的數量 n: 括號的數量 ret: 放置答案的陣列 cur_str: 當前的字串 """ if pos == 2*n: ret.append(cur_str) return if left < n: dfs(pos+1, left+1, right, n, ret, cur_str+'(') if right < n: dfs(pos+1, left, right+1, n, ret, cur_str+')')
這個程式遍歷執行之後還沒有結束,我們還需要判斷生成出來的括號是否合法,也就是說括號需要匹配上。我們可以用一個棧來判斷括號是否能夠匹配,比如我們遇見左括號就進棧,遇見右括號則判斷棧頂,如果棧頂是左括號,那麼棧頂的左括號出棧,否則則入棧,最後判斷棧是否為空。這個演算法實現當然不難,但是如果你仔細去想了,你會發現完全沒有必要用棧,因為如果我們遇到右括號的時候,棧頂不為左括號,那麼一定最後是無法匹配的。因為後面出現的左括號不能匹配前面出現的右括號,正所謂往者不可追就是這個道理。【狗頭】
優化
我們來思考一個問題:什麼情況會出現右括號遇不到左括號呢?只有一種情況,就是當前出現右括號的個數超過了左括號,也就是說我們遍歷一下字串,如果中途出現右括號數量超過左括號的情況,那麼就說明這個字串是非法的。看起來沒毛病對吧,但是有問題,我們為什麼不在列舉的時候就判斷呢,如果左括號放入的數量已經等於右括號了,那麼就不往裡防止右括號,這樣不就可以保證搜尋到的一定是合法的字串嗎?
如果你能想到這一層,說明你對搜尋的理解已經很不錯了。我們看一下改動之後的程式碼:
def dfs(pos, left, right, n, ret, cur_str):
"""
pos: 當前列舉的位置
left: 已經放置的左括號的數量
right: 已經放置的右括號的數量
n: 括號的數量
ret: 放置答案的陣列
cur_str: 當前的字串
"""
if pos == 2*n:
ret.append(cur_str)
return
if left < n:
dfs(pos+1, left+1, right, n, ret, cur_str+'(')
if right < n and right < left:
dfs(pos+1, left, right+1, n, ret, cur_str+')')
大部分程式碼都沒有變化,只是在right < n後面加入了一個right < left這個條件。看似只有一個條件,但是這個條件起到的作用至關重要。整個演算法的效率有了質的提升,實際上這也是效率最高的演算法。
構造
上面的方案在LeetCode官方當中都有收入,也是比較常規的解法,下面要介紹的方法是我的原創,我個人感覺也比較有意思,分享給大家。
在之前的文章當中我們介紹過分治法,分治法的核心是將一個看似無法求解的大問題,分解成比較容易解決的小問題,最後加以解決。這道題當中,我們直接求n時的解法是比較困難的,沒辦法直接獲得,我們能不能也試著使用分治的方法來解決呢?
我們來觀察一下資料,當n=1的時候,很簡單,結果是(),只有這一種。當n=2呢?有兩種,分別是(())和()(),當n=3呢?有5種:((())), ()(()), ()()(), (()()), (())()。這當中有沒有規律呢?
我們用solution(n)表示n對應的解法,那麼我們可以寫出solution(n)對應的公式:
\[solution(n) = \sum_{i=1}^{n-1} solution(i)+solution(n-i) + ( + solution(n-1) + )\]
上面這個式子有點像是動態規劃的狀態轉移方程,雖然不完全一樣,但是大概是那麼回事。也就是說我們可以用比答案規模小的答案組裝成現在的答案。比如n=3時的答案,等於n=2時的答案和n=1時答案的拼接。
比如: solution(1) + solution(2) 可以得到: ()()()和()(()),solution(2) + solution(1)可以得到 ()()()和(())()。但是還有一種答案無法通過拼接得到就是( solution(2) )。也就是說在solution(2)的答案外面包一層括號。那為什麼不用考慮solution(1)的答案外面包兩層括號呢?答案很簡單,因為solution(2)已經包括了這樣的情況,所以我們只用往下考慮一層。
不過還沒有結束,還有一點小問題,就是這樣得到的答案可能會有重複,所以我們需要去重,利用set我們可以很簡單做到這點,讓我們一起來看程式碼:
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
solutionMap = {}
# 記錄下n=0和1時的答案
solutionMap[0] = set([""])
solutionMap[1] = set(["()"])
# 遍歷小於n的所有長度
for i in range(2, n+1):
cur = set()
# 遍歷小於n的所有長度
for j in range(1, i):
# 構造答案
ans1 = solutionMap[j]
ans2 = solutionMap[i-j]
for s in ans1:
for t in ans2:
cur.add(s + t)
# 構造 ( solution(n-1) )這種答案
for s in solutionMap[i-1]:
cur.add("(" + s + ")")
solutionMap[i] = cur
return list(solutionMap[n])
在C++當中,這兩種方法的效率差不多,但是使用Python的話,構造的方法要更快一些。和搜尋這種方法相比,搜尋是不知道答案去搜尋答案,而構造法是知道答案大概長什麼樣子,依據一定的規則生產答案。可以說是兩種不同思路的解法,也是我本人很喜歡這道題的原因。
這道題的程式碼都不長,但是思路挺有意思,希望大家會喜歡。
今天的文章就是這些,如果覺得有所收穫,請順手掃碼點個關注吧,你們的舉手之勞對我來說很重要。
相關推薦
LeetCode22 生成所有括號對
本文始發於個人公眾號:TechFlow,原創不易,求個關注 連結 Generate Parentheses 難度 Medium 描述 Given n pairs of parentheses, write a function to generate all combinations of well-
給定n對括號,編寫一個函式來生成正確括號的所有組合。
本題源自leetcode ----------------------------------------------------------------------------------------------- 思路: 1 用回溯法。用變數m 表示左括號的數量。n
n個括號對的所有可能情況
括號 main color 思路 出棧 gin col r+ div 所有可能情況的數量為卡特蘭數。故求所有可能的出棧情況與此類似。 思路: 若左括號沒全插入,則插入左括號; 若已插入左括號數比已插入右括號數多,則插入右括號; 1 #include<st
生成括號 給出 n 代表生成括號的對數,請你寫出一個函式,使其能夠生成所有可能的並且有效的括號組合。C++
核心是必須要先有一個左括號才能給字串新增括號,且無論何時右括號的個數一定要小於等於左括號的個數 而且函式引數最好不要使用引用,方便臨時變數的賦值。 C++程式碼如下 class Solution { public: vector<string> generateP
22.生成所有的括號組合
Generate Parentheses 問題描述: Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses
[Leetcode] valid parentheses 有效括號對
true class mine int min () etc strong bracket Given a string containing just the characters‘(‘,‘)‘,‘{‘,‘}‘,‘[‘and‘]‘, determine if the in
在 n 道題目中挑選一些使得所有人對題目的掌握情況不超過一半。
als main equal 超過一半 %d each amp preview 掌握 Snark and Philip are preparing the problemset for the upcoming pre-qualification round for se
IntelliJ IDEA設置代碼括號對齊方式
har size pop class post 括號 成功 tar 圖片 IntelliJ IDEA設置代碼括號對齊方式 IntelliJ IDEA默認的對齊方式如下:括號跟函數名在一行 想改為括號獨自占一行,如下: 配置方式如下:File
hibernate框架學習筆記4:主鍵生成策略、對象狀態
alt rri gen 線程安全 理論 微軟 unit conf lose 創建一個實體類: package domain; public class Customer { private Long cust_id; private Stri
ssl(https)介紹、實驗環境生成密鑰對、nginx配置SSL、https
ssl nginx配置ssl ssl介紹 https 生成SSL密鑰對 ssl原理 http與https區別 http默認端口為80,https默認端口為443;http傳輸數據為明文,https傳輸數據是加密的; http是HTTP協議運行在TCP之上。所有傳輸的內容都是明文,客戶端
Intellij IDEA 生成返回值對象快捷鍵
不必要 -- xtra style 需要 實現 alt ctr ext 在編寫一行JAVA語句時,有返回值的方法已經決定了返回對象的類型和泛型類型,我們只需要給這個對象起個名字就行。 如果使用快捷鍵生成這個返回值,我們就可以減少不必要的打字和思考,專註於過程的實現。 步驟:
nginx負載均衡、nginx ssl原理及生成密鑰對、nginx配制ssl
alt self. 加密傳輸 remote cat nginx ssl 之間 PE www. 1、nginx負載均衡 新建一個文件:vim /usr/local/nginx/conf/vhost/load.conf寫入: upstream abc_com{ ip_ha
Java之生成Pdf並對Pdf內容操作
enc images sub als tar 應用 throw mave add 雖說網上有很多可以在線導出Pdf或者word或者轉成png等格式的工具,但是我覺得還是得了解知道是怎麽實現的。一來,在線免費轉換工具,是有容量限制的,達到一定的容量時,是不能成功導出的;二來,
【資料結構】所有頂點對的最短路徑 Floyd演算法
所有頂點對的最短路徑問題是指:對於給定的有向圖G=(V,E),求任意一對頂點之間的最短路徑。 可以求解得到的 的遞推公式: #include <stdio.h> #include <stdlib.h> const int FINI
openssl生成祕鑰對
openssl genrsa -out pri.pem 1024 openssl rsa -in pri.pem -out pub.pem -pubout 這樣就生成祕鑰對了,其中pri.pem是私鑰,pub.pem是公鑰 比如當前目錄有一個檔案叫test.txt 加密test.txt
python3 RSA演算法生成祕鑰對、檔案加密解密
RSA檔案加密解密生成祕鑰對檔案加密檔案解密 生成祕鑰對 @staticmethod def create_rsa_keys(code='nooneknows'): # 生成 2048 位的 RSA 金鑰 key
LeetCode:22. Generate Parentheses(生成匹配括號)
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. For example, given n = 3
MTGAN:通過多工三元生成對抗性網路對說話人進行驗證
MTGAN: Speaker Verification through Multitasking Triplet Generative Adversarial Networks MTGAN:通過多工三元生成對抗性網路對說話人進行驗證 摘要 在本文中,我們提出了一種增強的三元組方法,它通過
使用棧實現可配置的括號對齊以及棧的實現原理
丟擲問題:校驗字串中括號是否對應,並可以根據配置修改需要匹配的成對字元。 例:()()(()) OK ()[]{}{([])} OK ((())] NO 對應則返回true,若不對應則返回false C#程式碼如下(與java程式碼差距不大),使用棧(新進後出
Floyd-Warshall 所有結點對的最短路徑演算法
以下程式碼僅支援結點是順序的,比如輸入5個結點,結點的編號只能是1到5,輸入順序可以不一致。 動態規劃真的簡潔,三個 for 把這麼複雜的東西就整理好了。 遞推公式:**d[i][j] = min ( d[i][j] , d[i][k] + d[k][j] )