[{"data":1,"prerenderedAt":580},["ShallowReactive",2],{"blog-\u002Fblog\u002Fleetcode_valid_parentheses":3},{"id":4,"title":5,"body":6,"category":564,"cover":565,"csdn":565,"date":566,"description":567,"draft":568,"extension":569,"meta":570,"navigation":235,"path":571,"seo":572,"stem":573,"tags":574,"updated":565,"__hash__":579},"blog\u002Fblog\u002Fleetcode_valid_parentheses.md","LeetCode 20：有效的括号（暴力法与栈）",{"type":7,"value":8,"toc":554},"minimark",[9,17,21,49,52,65,68,163,167,180,189,196,206,209,296,303,319,322,326,329,359,497,502,512,515,527,530,533,536,546,550],[10,11,12],"p",{},[13,14],"img",{"alt":15,"src":16},"有效的括号示意图","https:\u002F\u002Fpub-5ec96507162a4f0a8e713a916117c9f4.r2.dev\u002Fblog\u002F55255b28703cca6143b9e759fca9f070.jpg",[18,19,20],"h2",{"id":20},"题目描述",[10,22,23,24,28,29,28,32,28,35,28,38,28,41,44,45,48],{},"给定一个只包含 ",[25,26,27],"code",{},"(","、",[25,30,31],{},")",[25,33,34],{},"[",[25,36,37],{},"]",[25,39,40],{},"{",[25,42,43],{},"}"," 的字符串 ",[25,46,47],{},"s","，判断字符串中的括号是否有效。",[10,50,51],{},"有效字符串需要满足：",[53,54,55,59,62],"ol",{},[56,57,58],"li",{},"左括号必须由相同类型的右括号闭合；",[56,60,61],{},"左括号必须按照正确的顺序闭合；",[56,63,64],{},"每个右括号都必须有一个与之对应的左括号。",[10,66,67],{},"例如：",[69,70,71,87],"table",{},[72,73,74],"thead",{},[75,76,77,81,84],"tr",{},[78,79,80],"th",{},"输入",[78,82,83],{},"输出",[78,85,86],{},"说明",[88,89,90,106,120,135,149],"tbody",{},[75,91,92,98,103],{},[93,94,95],"td",{},[25,96,97],{},"()",[93,99,100],{},[25,101,102],{},"true",[93,104,105],{},"一对圆括号正确闭合",[75,107,108,113,117],{},[93,109,110],{},[25,111,112],{},"()[]{}",[93,114,115],{},[25,116,102],{},[93,118,119],{},"三组括号均正确闭合",[75,121,122,127,132],{},[93,123,124],{},[25,125,126],{},"(]",[93,128,129],{},[25,130,131],{},"false",[93,133,134],{},"括号类型不匹配",[75,136,137,142,146],{},[93,138,139],{},[25,140,141],{},"([)]",[93,143,144],{},[25,145,131],{},[93,147,148],{},"括号的闭合顺序错误",[75,150,151,156,160],{},[93,152,153],{},[25,154,155],{},"{[]}",[93,157,158],{},[25,159,102],{},[93,161,162],{},"嵌套顺序正确",[18,164,166],{"id":165},"解法一暴力消除","解法一：暴力消除",[10,168,169,170,28,172,175,176,179],{},"不断删除字符串中相邻且匹配的 ",[25,171,97],{},[25,173,174],{},"[]"," 和 ",[25,177,178],{},"{}","：",[181,182,183,186],"ul",{},[56,184,185],{},"如果最后字符串变为空，说明所有括号都能正确匹配；",[56,187,188],{},"如果某一轮没有删除任何内容，但字符串仍不为空，说明字符串无效。",[10,190,191,192,195],{},"以 ",[25,193,194],{},"{[()]}"," 为例：",[197,198,204],"pre",{"className":199,"code":201,"language":202,"meta":203},[200],"language-text","{[()]}  ->  {[]}  ->  {}  ->  空字符串\n","text","",[25,205,201],{"__ignoreMap":203},[10,207,208],{},"JavaScript 实现：",[197,210,214],{"className":211,"code":212,"language":213,"meta":203,"style":203},"language-js shiki shiki-themes github-light","var isValid = function (s) {\n    let previousLength = -1;\n\n    while (s.length !== previousLength) {\n        previousLength = s.length;\n        s = s\n            .replaceAll(\"()\", \"\")\n            .replaceAll(\"[]\", \"\")\n            .replaceAll(\"{}\", \"\");\n    }\n\n    return s.length === 0;\n};\n","js",[25,215,216,224,230,237,243,249,255,261,267,273,279,284,290],{"__ignoreMap":203},[217,218,221],"span",{"class":219,"line":220},"line",1,[217,222,223],{},"var isValid = function (s) {\n",[217,225,227],{"class":219,"line":226},2,[217,228,229],{},"    let previousLength = -1;\n",[217,231,233],{"class":219,"line":232},3,[217,234,236],{"emptyLinePlaceholder":235},true,"\n",[217,238,240],{"class":219,"line":239},4,[217,241,242],{},"    while (s.length !== previousLength) {\n",[217,244,246],{"class":219,"line":245},5,[217,247,248],{},"        previousLength = s.length;\n",[217,250,252],{"class":219,"line":251},6,[217,253,254],{},"        s = s\n",[217,256,258],{"class":219,"line":257},7,[217,259,260],{},"            .replaceAll(\"()\", \"\")\n",[217,262,264],{"class":219,"line":263},8,[217,265,266],{},"            .replaceAll(\"[]\", \"\")\n",[217,268,270],{"class":219,"line":269},9,[217,271,272],{},"            .replaceAll(\"{}\", \"\");\n",[217,274,276],{"class":219,"line":275},10,[217,277,278],{},"    }\n",[217,280,282],{"class":219,"line":281},11,[217,283,236],{"emptyLinePlaceholder":235},[217,285,287],{"class":219,"line":286},12,[217,288,289],{},"    return s.length === 0;\n",[217,291,293],{"class":219,"line":292},13,[217,294,295],{},"};\n",[10,297,298,299,302],{},"每轮替换都需要遍历字符串，最坏情况下还要执行约 ",[25,300,301],{},"n \u002F 2"," 轮，因此：",[181,304,305,312],{},[56,306,307,308,311],{},"时间复杂度：",[25,309,310],{},"O(n²)","；",[56,313,314,315,318],{},"空间复杂度：",[25,316,317],{},"O(n)","，因为 JavaScript 字符串不可变，每次替换都会生成新字符串。",[10,320,321],{},"这种写法容易理解，但会重复遍历字符串，不适合作为最优解。",[18,323,325],{"id":324},"解法二栈","解法二：栈",[10,327,328],{},"括号匹配具有明显的“后进先出”特点：最后出现的左括号，必须最先被对应的右括号闭合。因此可以用数组模拟栈：",[53,330,331,341,351,356],{},[56,332,333,334,28,336,28,338,340],{},"遇到左括号 ",[25,335,27],{},[25,337,34],{},[25,339,40],{},"，将它压入栈中；",[56,342,343,344,28,346,28,348,350],{},"遇到右括号 ",[25,345,31],{},[25,347,37],{},[25,349,43],{},"，检查它是否与栈顶的左括号匹配；",[56,352,353,354,311],{},"匹配则弹出栈顶元素，否则立即返回 ",[25,355,131],{},[56,357,358],{},"遍历结束后，只有栈为空时才说明全部括号都完成了匹配。",[197,360,362],{"className":211,"code":361,"language":213,"meta":203,"style":203},"var isValid = function (s) {\n    const stack = [];\n    const pairs = new Map([\n        [\"(\", \")\"],\n        [\"[\", \"]\"],\n        [\"{\", \"}\"]\n    ]);\n\n    for (const char of s) {\n        if (pairs.has(char)) {\n            stack.push(char);\n            continue;\n        }\n\n        if (stack.length === 0) {\n            return false;\n        }\n\n        const leftBracket = stack.pop();\n        if (pairs.get(leftBracket) !== char) {\n            return false;\n        }\n    }\n\n    return stack.length === 0;\n};\n",[25,363,364,368,373,378,383,388,393,398,402,407,412,417,422,427,432,438,444,449,454,460,466,471,476,481,486,492],{"__ignoreMap":203},[217,365,366],{"class":219,"line":220},[217,367,223],{},[217,369,370],{"class":219,"line":226},[217,371,372],{},"    const stack = [];\n",[217,374,375],{"class":219,"line":232},[217,376,377],{},"    const pairs = new Map([\n",[217,379,380],{"class":219,"line":239},[217,381,382],{},"        [\"(\", \")\"],\n",[217,384,385],{"class":219,"line":245},[217,386,387],{},"        [\"[\", \"]\"],\n",[217,389,390],{"class":219,"line":251},[217,391,392],{},"        [\"{\", \"}\"]\n",[217,394,395],{"class":219,"line":257},[217,396,397],{},"    ]);\n",[217,399,400],{"class":219,"line":263},[217,401,236],{"emptyLinePlaceholder":235},[217,403,404],{"class":219,"line":269},[217,405,406],{},"    for (const char of s) {\n",[217,408,409],{"class":219,"line":275},[217,410,411],{},"        if (pairs.has(char)) {\n",[217,413,414],{"class":219,"line":281},[217,415,416],{},"            stack.push(char);\n",[217,418,419],{"class":219,"line":286},[217,420,421],{},"            continue;\n",[217,423,424],{"class":219,"line":292},[217,425,426],{},"        }\n",[217,428,430],{"class":219,"line":429},14,[217,431,236],{"emptyLinePlaceholder":235},[217,433,435],{"class":219,"line":434},15,[217,436,437],{},"        if (stack.length === 0) {\n",[217,439,441],{"class":219,"line":440},16,[217,442,443],{},"            return false;\n",[217,445,447],{"class":219,"line":446},17,[217,448,426],{},[217,450,452],{"class":219,"line":451},18,[217,453,236],{"emptyLinePlaceholder":235},[217,455,457],{"class":219,"line":456},19,[217,458,459],{},"        const leftBracket = stack.pop();\n",[217,461,463],{"class":219,"line":462},20,[217,464,465],{},"        if (pairs.get(leftBracket) !== char) {\n",[217,467,469],{"class":219,"line":468},21,[217,470,443],{},[217,472,474],{"class":219,"line":473},22,[217,475,426],{},[217,477,479],{"class":219,"line":478},23,[217,480,278],{},[217,482,484],{"class":219,"line":483},24,[217,485,236],{"emptyLinePlaceholder":235},[217,487,489],{"class":219,"line":488},25,[217,490,491],{},"    return stack.length === 0;\n",[217,493,495],{"class":219,"line":494},26,[217,496,295],{},[498,499,501],"h3",{"id":500},"为什么最后还要判断栈是否为空","为什么最后还要判断栈是否为空？",[10,503,504,505,508,509,511],{},"例如输入 ",[25,506,507],{},"((("," 时，遍历过程中不会遇到不匹配的右括号，但仍有三个左括号没有闭合。此时栈不为空，所以结果应该是 ",[25,510,131],{},"。",[498,513,514],{"id":514},"复杂度分析",[181,516,517,522],{},[56,518,307,519,521],{},[25,520,317],{},"，每个字符只会入栈或出栈一次；",[56,523,314,524,526],{},[25,525,317],{},"，最坏情况下字符串全部由左括号组成。",[18,528,529],{"id":529},"总结",[10,531,532],{},"暴力消除法比较直观，适合理解括号匹配的过程；栈解法只需遍历一次字符串，效率更高，也更符合这道题“后进先出”的结构特征。",[10,534,535],{},"如果文章对你有帮助，欢迎点赞；如果发现错误或有其他思路，也欢迎留言交流。",[10,537,538,539],{},"我的个人网站：",[540,541,545],"a",{"href":542,"rel":543},"https:\u002F\u002Fhongweizhu.com",[544],"nofollow","hongweizhu.com",[18,547,549],{"id":548},"end","END",[551,552,553],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":203,"searchDepth":226,"depth":226,"links":555},[556,557,558,562,563],{"id":20,"depth":226,"text":20},{"id":165,"depth":226,"text":166},{"id":324,"depth":226,"text":325,"children":559},[560,561],{"id":500,"depth":232,"text":501},{"id":514,"depth":232,"text":514},{"id":529,"depth":226,"text":529},{"id":548,"depth":226,"text":549},"算法",null,"2021-01-11","使用 JavaScript 解决 LeetCode 20 有效的括号，并分析暴力消除法与栈解法的思路和复杂度。",false,"md",{},"\u002Fblog\u002Fleetcode_valid_parentheses",{"title":5,"description":567},"blog\u002Fleetcode_valid_parentheses",[575,576,577,578],"LeetCode","JavaScript","栈","字符串","wXjH87c3N-lRFe5AEya4KBsJ-H7Rv2Jro03uVelL2sU",1785517647122]