栈
栈是一种特殊的线性表, 仅能够在栈顶进行操作, 有着先进后出的特性。
栈的简单实现
- 实现栈可以有两种方式,一种是以数组做基础;一种是以链表做基础。
- 栈的方法
- push (添加一个元素到栈顶)
- pop (弹出栈顶元素)
- top (返回栈顶元素, 注意不是弹出)
- isEmpty (判断栈是否为空)
- size (返回栈里元素的个数)
- clear (清空栈)
function Stack {
var items = []
this.push = function (item) {
items.push(item)
}
this.pop = function() {
return items.pop()
}
this.top = function() {
return items[items.length - 1]
}
this.isEmpty = function() {
return !items.length
}
this.size = function () {
return items.length
}
this.clear = function() {
items = []
}
}
应用练习
1. 编写一个函数判断字符串中的括号是否合法, 所谓合法,就是括号成对出现。
sff()sf(sasfa)sfa(sdfsfd(sf)) 合法
((asf)sf(af)fa) 合法
)(sfa)f 不合法
(saf))fafa(sss() 不合法
做前思路分析:
- for循环遍历字符串每一个字符
- 遇到左括号(, 就把左括号压入栈中
- 遇到右括号,判断栈是否为空, 为空说明没有左括号与之对应, 缺少左括号, 字符串括号不合法,如果栈不为空,则把栈顶元素弹出,这对括号抵消掉
- for 循环结束之后, 如果栈是空的, 说明左右括号抵消掉了,字符串合法。如果栈里还有元素,则说明缺少右括号,字符串不合法
- 边界情况处理
a.字符串为空
b.字符串只有一个或者奇数个
代码实现
function is_leagl_brackets(str) {
// -----------5.a------------
if(!str) return false;
// -----------5.b------------
if(str.length % 2 === 1) return false
// 使用上面实现的栈
const stack = new Stack();
// ------------1-------------
for(let i = 0; i < str.length; i++) {
const item = str[i]
// ------------2----------
if(item === '(') {
stack.push(item)
}
// --------------3----------
if(item === ')') {
if(stack.isEmpty()) {
return false
}else {
stack.pop()
}
}
}
// ------------4-----------
return stack.isEmpty()
}
- 时间复杂度: O(N) 每个字符只入栈一次,出栈一次
- 空间复杂度:O(N) 最差的情况下会把整个字符串入栈
做后总结分析
- 还能怎么优化
- 这种解法具有普适性吗
- 栈中存放的元素是一样的都是 '(', 实际上没有必要使用栈,只需记录元素个数,入栈加 1 出栈减 1 就行了
function is_leagl_brackets(str) {
if(!str) return false;
if(str.length % 2 === 1) return false
// 计数处理
let count = 0;
for(let i = 0; i < str.length; i++) {
const item = str[i]
if(item === '(') {
count++
}
if(item === ')') {
if(count === 0) {
return false
}else {
count--
}
}
}
return count === 0
}
- 时间复杂度: O(N) 每个字符只入栈一次,出栈一次
- 空间复杂度:O(1) 只有了一个变量来记录栈中的内容
20.有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。
- 逆波兰表达式
也叫后缀表达式,它将复杂表达式转换为可以依靠简单的操作得到计算结果的表达式, 例如 (a + b) * (c + d) 转换为a b + c d + *
["4", "13", "5", "/", "+"] 等价于(4 + (13 / 5)) = 6
["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"] 等价于((10 *
(6/((9 + 3) * -11 ))) + 17) + 5
思路:
for 循环遍历数组,对每一个元素做如下操作
- 如果元素不是 + - * / 中某一个,直接压入栈。
- 如果是 + - / *中的一个,则连续弹出两个元素,并对这两个元素进行计算,且第一元素在运算符右侧, 第二个在运算符左侧,并把计算结果压入栈。
for 循环结束后, 栈里只有一个元素, 则这个元素就是整个表达式的计算结果。
代码实现
const operators = ['+', '-', '*', '/']
function calc_exp(exp_arr){
const stack = new Stack()
for(let i = 0; i < exp_arr.length; i++) {
const item = exp_arr[i]
if(operators.includes(item)) {
// 从栈顶弹出连个元素
const val1 = stack.pop()
const val2 = stack.pop()
// 拼成表达式
const str = val2 + item + val1
const result = parseInt(eval(str))
// 将计算结果转成字符串并压入栈
stack.push(result.toString())
}else {
stack.push(item)
}
}
// for 循环结束, 栈里只有一个元素, 就是最终的结果。
return stack.pop()
}