JavaScript栈数据结构算法思想
栈的介绍
- 栈(stack)又名堆栈,它是一种运算受限的线性表,仅在表尾能进行插入和删除操作。这一端被称为栈顶,相对的另一端被称为栈尾。
- 向一个栈中插入一个元素又称作进栈、入栈或压栈;从一个栈删除元素又称作出栈或退栈。
- 后进先出(LIFO)特点:栈中的元素,最先进栈的必定最后出栈,后进栈的一定会先出栈。
JavaScript中,栈可以用数组模拟。需要限制数组只能使用push()和pop()方法,不能使用unshift()和shift()方法。即为,数组尾就是栈顶。当然用面向对象等手段,将栈封装会更好。
下面用mustache模板引擎的部分原理来举个例子:
const domstr = `<div>
<ol>
{{#students}}
<li>
学生{{name}}的爱好是
<ol>
{{#hobbies}}
<li>{{.}}</li>
{{/hobbies}}
</ol>
</li>
{{/students}}
</ol>
</div>`
[ // 该数组是由上面的 dom 字符串处理所得,具体处理这里不做讲解
[ "text", "<div><ol>" ],
[ "#", "students"],
[ "text", "<li>学生" ],
[ "name", "name" ],
[ "text", "的爱好是<ol>" ],
[ "#", "hobbies"],
[ "text", "<li>" ],
[ "name", "." ],
[ "text", "</li>" ],
[ "/", "hobbies" ],
[ "text", "</ol></li>" ],
[ "/", "students" ],
[ "text", "</ol></div>" ]
]
现在要做的就是按照规律,把上面的数组变为有层级关系的数组,如下面所示:
[
[ "text", "<div><ol>" ],
[ "#", "students", [
[ "text", "<li>学生" ],
[ "name", "name" ],
[ "text", "的爱好是<ol>" ],
[ "#", "hobbies", [
[ "text", "<li>" ],
[ "name", "." ],
[ "text", "</li>" ]
]],
[ "text", "</ol></li>" ]
]],
[ "text", "</ol></div>" ]
]
具体实现思路,就用了栈数据结构算法思想,因为栈结构的入栈出栈可以形成层级结构。层级最高的在栈底,依次向上,先对栈顶元素进行处理,处理完再整合到下一层,直到处理完毕,和递归有点相似。
具体代码如下:
function nestTokens(tokens) {
// 结果数组
var nestedTokens = []
// 栈容器
var sections = []
// 收集器,收集栈顶的元素,初始化为结果数组,因为其层级最高
var collector = nestedTokens
// 循环tokens中的每一项
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i] // 接受第i项
switch (token[0]) { // 判断层级结构的标志
case '#': // 为 # 时,入栈,增加一层层级
collector.push(token) // 标记栈顶元素
sections.push(token) // 元素入栈
collector = token[2] = [] // 向数组第三项添加层级
break
case '/': // 为 / 时,出栈,减少一层层级
let section = sections.pop() // 元素出栈
// 判断栈中是否还有元素,重新标记栈顶元素
collector = sections.length > 0 ? sections[sections.length - 1][2] : nestedTokens
break
default:
// 其他情况直接向栈顶添加元素即可,因为上述两种判断栈顶会跟随变化
collector.push(token)
}
}
// 返回结果数组
return nestedTokens
}
初学者大坑:栈的题目和递归非常像,这类题目感觉用递归解题,信心满满的开始写,结果发现递归怎么也递归不出来,此时就要想到,不是用递归,而是用栈!
个人分析用哪种的方法:
- 已经是对象或数组形式,有规律的层级结构,要转变为其他形式,优先考虑 递归
- 如果是字符串形式,拥有层级关系的规律,要转变为数组或对象形式的结构,优先考虑 栈