栈和队列都是比较常用的数据结构。栈的应用非常的广泛,比如说,递归函数的实现就是借助于栈保存相关的数据。操作系统中每个线程也会使用栈来保存函数调用涉及到的一些参数和其他变量等。栈最大的一个特点就是先进后出(FILO—First-In/Last-Out)。
队列和栈不同的是,队列是一种先进先出(FIFO—first in first out)的数据结构。
栈的相关方法:
入栈,s.push(x)
出栈,s.pop()
访问栈顶,s.top()
判断栈空,s.empty()
访问栈中的元素个数,s.size()
队列的方法与栈大同小异,列举如下:
入队,q.push(x)
出队,q.pop()
访问队首元素,q.front()、访问队尾元素,q.back()
判断队列空,q.empty()
访问队列中的元素个数,q.size()
栈的应用场景:
栈最大的特点是先进后出,要合理利用这一点,也就是在循环过程中,遇到暂时用不到的元素或数据,就先入栈,等合适时机再一一出栈。
1.逆序输出
2.语法检查,符号成对出现
3.数制转换
4.二叉树的一些操作
等等
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
这里不用考虑括号的优先级,即'([])'也是有效的
这里就可以用栈的思想:遍历字符串,如果是左括号就入栈,如果是右括号,就判断是否和栈顶的左括号是同类型,是就出栈,否则就返回false,循环结束后再去判断栈是否是空的,空的就返回true,否则返回false
var isValid = function(s) {
let stack = []
for (let i = 0; i < s.length; i++) {
if(s[i]=="("){
stack.push(")");
}else if(s[i]=="["){
stack.push("]");
}else if(s[i]=="{"){
stack.push("}")
}else if(stack.pop()!=s[i]){
return false;
}
}
return !stack.length
};
这里的if else有点多,感觉不够优雅,可以用哈希表来优化下
var isValid = function(s) {
let stack = []
let dic = {'(':')','[':']','{':'}'}
for (let i = 0; i < s.length; i++) {
if(s[i] in dic) stack.push(s[i])
else if(dic[stack.pop()] != s[i]) return false
}
return !stack.length
};
根据每日 气温 列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高的天数。如果之后都不会升高,请输入 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的都是 [30, 100] 范围内的整数。
比较容易想到的就是暴力循环
var dailyTemperatures = function(T) {
let res = []
for(let i = 0; i < T.length; i++){
let num = 0
for(let j = i + 1; j < T.length; j++){
if(T[j] > T[i]){
num = j - i
break
}
}
res.push(num)
}
return res
};
但这种时间复杂度是o(n^2),leetcode上用js跑大概1000ms的样子,并不理想
这里我们可以用栈的思想,遍历数组,将索引入栈,遍历栈,如果元素比栈顶索引对应元素的大,则就可以直接出栈,否则就继续遍历数组
var dailyTemperatures = function(T) {
let res = Array(T.length).fill(0)
let stack = []
for(let i = 0; i < T.length; i++){
if(stack.length){
while(stack.length && T[stack[stack.length - 1]] < T[i]){
res[stack[stack.length - 1]] = i - stack.pop()
}
}
stack.push(i)
}
return res
};
这样就大大缩短了时间,leetcode上用js跑大概100多ms
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
示例:
s = "3[a]2[bc]", 返回 "aaabcbc".
s = "3[a2[c]]", 返回 "accaccacc".
s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".
从题目上,很容易可以看出用栈来解题比较合适
遍历字符串,在没有遇到']'前就先入栈,遇到']'后,反向遍历栈,直到找到'[',将'[]'之间的字符串提取出来,在继续遍历栈,提取出'['前的数字nums,注意该数字nums可能是多位数,提取完后,将提取出来的字符串入栈nums次,继续向后遍历。
遍历结束后,将栈转成字符串返回即可
var decodeString = function(s) {
let res = []
for (const char of s) {
if(char == ']'){
let chars = [];let nums = []
while(res[res.length - 1] != '['){
chars.push(res.pop())
}
res.pop()
chars = chars.reverse().join('')
while(!isNaN(res[res.length - 1])) {
nums.push(res.pop());
}
nums = parseInt(nums.reverse().join(''))
for(let i = 0; i < nums; i++){
res.push(chars)
}
}
else res.push(char)
}
return res.join('')
};