有效的括号
【题目】: 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。
【示例1:】
输入: s = "()"
输出: true
【示例 2】:
输入: s = "()[]{}"
输出: true
【示例 3】:
输入: s = "(]"
输出: false
【示例 4】:
输入: s = "([)]"
输出: false
【示例 5】:
输入: s = "{[]}"
输出: true
【思考:】一对括号可以理解为栈的入栈和出栈,那么如果有一个有效字符串,表现为最终的栈就是一个空栈,用数组来实现,循环字符串,添加每个字符到数组中,如果出现相邻两个元素是一对括号,就去除这一对元素,剩下的数组中如果还有元素,则表示不是一个有效的字符串
【代码:】
/**
* @param {string} s
* @return {boolean}
*/
var isValid = function(s) {
let sMap = {
'(': 1,
')': -1,
'[': 2,
']': -2,
'{': 3,
'}': -3
}
let ret = []
for(let i = 0; i<s.length;i++){
let length = ret.length
if(length && sMap[ret[length-1]] > 0 && sMap[ret[length-1]]+sMap[s[i]]===0 ){
ret.pop()
}else{
ret.push(s[i])
}
}
if(ret.length){
return false
}
return true
};
删除最外层的括号
【题目】: 有效括号字符串为空 ""、"(" + 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:】
输入:s = "(()())(())"
输出:"()()()"
解释:
输入字符串为 "(()())(())",原语化分解得到 "(()())" + "(())",
删除每个部分中的最外层括号后得到 "()()" + "()" = "()()()"。
【示例 2】:
输入:s = "(()())(())(()(()))"
输出:"()()()()(())"
解释:
输入字符串为 "(()())(())(()(()))",原语化分解得到 "(()())" + "(())" + "(()(()))",
删除每个部分中的最外层括号后得到 "()()" + "()" + "()(())" = "()()()()(())"。
【示例 3】:
输入:s = "()()"
输出:""
解释:
输入字符串为 "()()",原语化分解得到 "()" + "()",
删除每个部分中的最外层括号后得到 "" + "" = ""。
【思考:】这个跟上面那题差不多,不过这个只存在一种小括号,就不用特意给每种括号元素定义值了,直接用一个计数器累计就可以
- 遇到左括号,我们的计数器 +1+1,遇到右括号,我们的计数器 -1−1。这样的话,一组连续且有效的括号,将不会对计数器的值产生变化。
【代码:】
/**
* @param {string} s
* @return {string}
*/
var removeOuterParentheses = function(s) {
let con = 0, ret = ''
for(let i = 0; i < s.length; i++){
if(s[i]==='(' && con++>0) ret+=s[i]
if(s[i]===')' && con-->1) ret+=s[i]
}
return ret
};
验证栈序列
【题目】: 给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false 。
【示例1:】
输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
【示例2:】
输入: pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出: false
解释: 1 不能在 2 之前弹出。
【提示:】
- 1 <= pushed.length <= 1000
- 0 <= pushed[i] <= 1000
- pushed 的所有元素 互不相同
- popped.length == pushed.length
- popped 是 pushed 的一个排列
【思考:】新建栈stack和计数器index,将pushed中元素依次推入栈,并循环popped,并判断stack中最后的元素和popped的index下标的元素是否相等,若相等,推出stack最后一个元素,同时计数器累加,最终stack中无元素,则返回true。
注意: popped中元素需判断是否存在,且其中0为有效值
【代码:】
/**
* @param {number[]} pushed
* @param {number[]} popped
* @return {boolean}
*/
var validateStackSequences = function(pushed, popped) {
let index = 0,stack = []
for(let i = 0;i < pushed.length; i++) {
stack.push(pushed[i])
while(popped[index]!==undefined && popped[index] === stack[stack.length-1]){
stack.pop()
index++
}
}
return !stack.length
};
移除无效的括号
【题目】: 给你一个由 '('、')' 和小写字母组成的字符串 s。
你需要从字符串中删除最少数目的 '(' 或者 ')' (可以删除任意位置的括号),使得剩下的「括号字符串」有效。
请返回任意一个合法字符串。
有效「括号字符串」应当符合以下 任意一条 要求:
- 空字符串或只包含小写字母的字符串
- 可以被写作 AB(A 连接 B)的字符串,其中 A 和 B 都是有效「括号字符串」
- 可以被写作 (A) 的字符串,其中 A 是一个有效的「括号字符串」
【示例1:】
输入:s = "lee(t(c)o)de)"
输出:"lee(t(c)o)de"
解释:"lee(t(co)de)" , "lee(t(c)ode)" 也是一个可行答案。
【示例2:】
输入: s = "a)b(c)d"
输出: "ab(c)d"
【示例3:】
输入: s = "))(("
输出: ""
解释: 空字符串也是有效的
【示例4:】
输入: s = "(a(b(c)d)"
输出: "a(b(c)d)"
【提示:】
1 <= s.length <= 10^5s[i]可能是'('、')'或英文小写字母
【方法:】首先首先定义一个空栈stack和包含所有字符的数组ret,用以保存括号,遍历字符串,利用入栈出栈去除ret中所有多余的‘)’,然后stack中只剩‘(’,此时再遍历stack去除ret多余的‘(’
【代码:】
/**
* @param {string} s
* @return {string}
*/
var minRemoveToMakeValid = function(s) {
let ret = [...s]
let stack = []
for(let i = 0; i < s.length; i++) {
if(ret[i]==='(') {
stack.push(i)
}
if(ret[i]===')') {
if(stack.length) {
stack.pop()
}else{
ret[i]=''
}
}
}
for(let i = 0; i< stack.length; i++){
ret[stack[i]] = ''
}
return ret.join('')
}
二叉树的后序遍历
【题目】: 给定一个二叉树,返回它的 后序 遍历
【示例1:】
输入: [1,null,2,3]
1
\
2
/
3
输出: [3,2,1]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
【思考:】后续遍历,即按照左右中的顺序遍历
- 递归法:此方法较为简单,就是先排左,再排右,最后中间,每个节点都以此方法调用
- 迭代法:首先设置一个栈stack,按照中右左保存每个节点,并在保存中节点时设置一个空标识位,设置答案空栈res,逆向循环stack中的节点,保存其中的标识位的下一个值,即为后序遍历的二叉树
【代码:】
// 递归法
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var postorderTraversal = function(root) {
let res = []
return _postorderTraversal(root, res)
};
function _postorderTraversal(root, res) {
if(!root) return res
_postorderTraversal(root.left, res)
_postorderTraversal(root.right, res)
res.push(root.val)
return res
}
// 迭代法
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @description 迭代法
* @param {TreeNode} root
* @return {number[]}
*/
var postorderTraversal = function(root) {
let stack = [],res = []
if(root) stack.push(root)
while(root!==null || stack.length!==0){
root = stack.pop()
if(root){
stack.push(root)
stack.push(null)
if(root.right) stack.push(root.right)
if(root.left) stack.push(root.left)
}else{
res.push(stack.pop().val)
}
}
return res
};