一 目录
不折腾的前端,和咸鱼有什么区别
目录 |
---|
一 目录 |
二 前言 |
三 解题及测试 |
四 LeetCode Submit |
五 解题思路 |
六 进一步思考 |
二 前言
难度:简单
涉及知识:栈
题目地址:https://leetcode-cn.com/problems/remove-outermost-parentheses/
题目内容:
有效括号字符串为空 ("")、
"(" + A + ")" 或 A + B,
其中 A 和 B 都是有效的括号字符串,
+ 代表字符串的连接。
例如,"","()","(())()" 和 "(()(()))",
都是有效的括号字符串。
如果有效字符串 S 非空,
且不存在将其拆分为 S = A+B 的方法,
我们称其为原语(primitive),
其中 A 和 B 都是非空有效括号字符串。
给出一个非空有效字符串 S,
考虑将其进行原语化分解,
使得:S = P_1 + P_2 + ... + P_k,
其中 P_i 是有效括号字符串原语。
对 S 进行原语化分解,
删除分解中每个原语字符串的最外层括号,返回 S 。
示例 1:
输入:"(()())(())"
输出:"()()()"
解释:
输入字符串为 "(()())(())",
原语化分解得到 "(()())" + "(())",
删除每个部分中的最外层括号后得到 "()()" + "()" = "()()()"。
示例 2:
输入:"(()())(())(()(()))"
输出:"()()()()(())"
解释:
输入字符串为 "(()())(())(()(()))",
原语化分解得到 "(()())" + "(())" + "(()(()))",
删除每隔部分中的最外层括号后得到 "()()" + "()" + "()(())" = "()()()()(())"。
示例 3:
输入:"()()"
输出:""
解释:
输入字符串为 "()()",
原语化分解得到 "()" + "()",
删除每个部分中的最外层括号后得到 "" + "" = ""。
提示:
S.length <= 10000
S[i] 为 "(" 或 ")"
S 是一个有效括号字符串
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-outermost-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
三 解题及测试
小伙伴可以先自己在本地尝试解题,再回来看看 jsliang 的解题思路。
LeetCode 给定函数体:
/**
* @param {string} S
* @return {string}
*/
var removeOuterParentheses = function(S) {
};
根据上面的已知函数,尝试破解本题吧~
确定了自己的答案再看下面代码哈~
index.js
/**
* @name 删除最外层的括号
* @param {string} S
* @return {string}
*/
const removeOuterParentheses = (S) => {
// 1. 设置数组获取原语
const primitive = [];
// 2. 初始化堆栈
let now = [S[0]];
// 3. 设置游标
let flag = 0;
// 4. 遍历函数
for (let i = 1; i < S.length; i++) {
// 4.1 如果当前的括号和栈顶元素一致,则添加,否则则推出
if (S[i] === now[now.length - 1]) {
now.push(S[i]);
} else {
now.pop();
}
// 4.2 如果 now 没长度了,说明当前是一个有效的括号了,进行一系列操作
if (!now.length) {
primitive.push(S.slice(flag, i + 1));
flag = i + 1;
now = [S[i + 1]];
i++;
}
}
// 5. 遍历原语,进行操作
for (let i = 0; i < primitive.length; i++) {
primitive[i] = primitive[i].slice(1, primitive[i].length - 1);
}
// 6. 将原语换成字符串
return primitive.join('');
};
console.log(removeOuterParentheses('(()())(())')); // ()()()
node index.js
返回:
()()()
四 LeetCode Submit
Accepted
* 59/59 cases passed (84 ms)
* Your runtime beats 19.7 % of javascript submissions
* Your memory usage beats 10.44 % of javascript submissions (37.5 MB)
五 解题思路
鲁迅曾经说过:
括号太多看不过来
讲真看得我有点眼花,我都要凑过去数括号了(近视加深 ing……)
吐槽完毕,开始解题:
存在一个有效的括号:
(()())(())
。首先我们需要对其原语化。什么是原语化?即对这个有效括号进行拆分,拆分出来的括号还是有效的:
(()())
+(())
。这时候,我们不能进一步拆分了,这样的操作就叫做原语化。然后我们去掉原语化后每个块的外层括号,变成:
()()
+()
。所以得到答案:
()()()
。
这样,答案呼之欲出:
暴力破解
const removeOuterParentheses = (S) => {
// 1. 设置数组获取原语
const primitive = [];
// 2. 初始化堆栈
let now = [S[0]];
// 3. 设置游标
let flag = 0;
// 4. 遍历函数
for (let i = 1; i < S.length; i++) {
// 4.1 如果当前的括号和栈顶元素一致,则添加,否则则推出
if (S[i] === now[now.length - 1]) {
now.push(S[i]);
} else {
now.pop();
}
// 4.2 如果 now 没长度了,说明当前是一个有效的括号了,进行一系列操作
if (!now.length) {
primitive.push(S.slice(flag, i + 1));
flag = i + 1;
now = [S[i + 1]];
i++;
}
}
// 5. 遍历原语,进行操作
for (let i = 0; i < primitive.length; i++) {
primitive[i] = primitive[i].slice(1, primitive[i].length - 1);
}
// 6. 将原语换成字符串
return primitive.join('');
};
Submit 提交:
Accepted
* 59/59 cases passed (84 ms)
* Your runtime beats 19.7 % of javascript submissions
* Your memory usage beats 10.44 % of javascript submissions (37.5 MB)
这是 jsliang 一开始的思路,也是最暴躁的思路,毕竟刚出社会的小伙伴不怕刚,直到社会磨平了他的棱角:
暴力破解【优化】
const removeOuterParentheses = (S) => {
const primitive = []; // 原语化数组
let nowPrimitive = [S[0]]; // 当前原语
const now = [S[0]]; // 当前堆栈
for (let i = 1; i < S.length; i++) {
// 堆栈操作
if (S[i] === now[now.length - 1]) {
now.push(S[i]);
} else {
now.pop();
}
// 原语操作
nowPrimitive.push(S[i]);
if (!now.length) {
now.push(S[i + 1]);
i++;
nowPrimitive.shift();
nowPrimitive.pop();
primitive.push(nowPrimitive.join(''));
nowPrimitive = [S[i + 1]];
}
}
return primitive.join('');
};
Submit 提交:
Accepted
* 59/59 cases passed (76 ms)
* Your runtime beats 40.66 % of javascript submissions
* Your memory usage beats 16.13 % of javascript submissions (37.1 MB)
六 进一步思考
接下来就是看看大佬操作了:
https://leetcode-cn.com/problems/remove-outermost-parentheses/solution/javascript-jian-dan-de-deltafa-bu-xu-yao-stack-by-/
delta 法
const removeOuterParentheses = (S) => {
let delta = 0;
let res = '';
for (const ch of S) {
if (ch === '(' && delta !== 0 || ch === ')' && delta !== 1) {
res += ch;
}
if (ch === '(') {
++delta;
} else if (ch === ')') {
--delta;
}
}
return res;
};
Submit 提交:
Accepted
* 59/59 cases passed (68 ms)
* Your runtime beats 78.28 % of javascript submissions
* Your memory usage beats 46.52 % of javascript submissions (36.2 MB)
至于这操作怎么理解,我也是第一次看啊~
你品,你细细地品~
如果小伙伴有更好的思路想法,欢迎评论留言或者私聊 jsliang~
不折腾的前端,和咸鱼有什么区别!
jsliang 会每天更新一道 LeetCode 题解,从而帮助小伙伴们夯实原生 JS 基础,了解与学习算法与数据结构。
浪子神剑 会每天更新面试题,以面试题为驱动来带动大家学习,坚持每天学习与思考,每天进步一点!
扫描上方二维码,关注 jsliang 的公众号(左)和 浪子神剑 的公众号(右),让我们一起折腾!
jsliang 的文档库 由 梁峻荣 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于https://github.com/LiangJunrong/document-library上的作品创作。
本许可协议授权之外的使用权限可以从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处获得。