1、概述
队列的主要两个操作是 enqueue(入队) 和 dequeue(出队):
入队和出队
栈的主要两个操作是 push(入栈) 和 pop(出栈):
入栈和出栈
因为这两个库底层都是基于链表完成(重操作、轻查询),所以复杂度和链表是一样的。
因为这两个库底层都是基于链表完成(重操作、轻查询),所以复杂度和链表是一样的。
队列比较常用的是广度优先遍历,在树中是层序遍历,在图中是无权图的最短路径; 栈能帮助你实现深度优先遍历等;
在 JS 中,队列和数组很相似,所以平时使用队列的场景会比较多;而对于栈这种数据结构接触的比较少,因此下面罗列的应用偏栈的应用比较多;
以下示例可以到 https://runkit.com/boycgit/ss-stack 查看具体的运行情况
来自文章 数据结构(三)之栈结构
要把十进制转化成二进制,我们可以将该十进制数字和2整除(二进制是满二进一),直到结果是0为止。举个例子,把十进制的数字10转化成二进制的数字,过程大概是这样:
二进制
var Stack = require("ss-stack");
// 封装十进制转二进制的函数
function dec2bin(decNumer) {
// 定义变量
var stack = new Stack()
var remainder;
// 循环除法
while (decNumer > 0) {
remainder = decNumer % 2;
decNumer = Math.floor(decNumer / 2);
stack.push(remainder);
}
// 将数据取出
var binayriStrng = ""
while (!stack.isEmpty()) {
binayriStrng += stack.pop();
}
return binayriStrng;
}
console.log(dec2bin(10));
console.log(dec2bin(233));
console.log(dec2bin(1000));
示例来自 JS括号匹配问题
var Stack = require("ss-stack");
function validBraces(braces){
let leftBraReg = /[\(\{\[]/,
// 栈
rightBracket
braces = braces.split('')
let stack = new Stack();
for(let bracket of braces) {
if(leftBraReg.test(bracket)) {
stack.push(bracket)
}
else {
switch (bracket) {
case ')':
rightBracket = stack.pop()
if(rightBracket !=='(') {
return false
}
break
case ']':
rightBracket = stack.pop()
if(rightBracket !=='[') {
return false
}
break
case '}':
rightBracket = stack.pop()
if(rightBracket !=='{') {
return false
}
break
}
}
}
return stack.length === 0 ? true : false
}
validBraces("(){}[]") // true
validBraces("(}") // false
validBraces("[(])") // false
validBraces("([{}])") // true
整个算法的思路是:
汉诺塔的递归法优雅而精妙,伪代码如下:
//将n个盘子按规则从a柱子,移动到c柱子
hanoi( n, a, b, c )
{
if( n > 0 )
{
hanoi(n-1,a,c,b);
//将a柱子的最上面的盘子移到c柱子
c.push( a.pop() );
hanoi(n-1,b,a,c);
}
}
代码来自 JavaScript数据结构与算法——栈及其应用
var Stack = require("ss-stack");
var towerOfHanoi = function(n, from, help, to) {
if (n > 0) {
towerOfHanoi(n-1, from, to, help);
to.push(from.pop());
console.log('----------------');
console.log(`from: ${from.stack.toArray()}`);
console.log(`help: ${help.stack.toArray()}`);
console.log(`to: ${to.stack.toArray()}`);
towerOfHanoi(n-1, help, from, to);
}
}
var a = new Stack(),
b = new Stack(),
c = new Stack(),
n = 4;
for(let i = n; i > 0; i--) {
a.push(i);
}
// test
towerOfHanoi(a.length, a, b, c);
代码来自 算法 - 调度场算法(Shunting Yard Algorithm)
/* ----------------------------------------------------
调度场算法,将中缀转换过程后缀表达式
----------------------------------------------------- */
var Stack = require("ss-stack");
// 操作符优先级列表
var PRI_LEFT = 99;
var PRI_RIGHT = 100;
var PRI_TOKEN_MAP = {
'+': 1,
'-': 1,
'*': 2,
'/': 2,
'(': PRI_LEFT,
')': PRI_RIGHT,
};
// 将中序转换成
function infixToPostfix(expression){
expression = expression.replace(/\s*/g,""); // 去除所有空白字符
var opStack = new Stack();
var str = '';
// 符号栈操作
function calcToken(){
str += opStack.pop();
}
for(let token of expression){
var tokenPri = PRI_TOKEN_MAP[token] || 0;
var peekTokenPri = opStack.peek && PRI_TOKEN_MAP[opStack.peek] || 0;
// console.log(opStack.stack.toArray());
// 首先判断是否操作符
if(tokenPri){
// 情况 1: 栈顶不是左括号;入栈符号不是右括号
// 循环判断当前栈顶符号,且优先级高于或等于将要入栈的元素,且栈顶不是左括号
while(opStack.peek && PRI_TOKEN_MAP[opStack.peek] !== PRI_LEFT && tokenPri !== PRI_RIGHT && peekTokenPri >= tokenPri) {
calcToken();
}
// 情况2:右括号准备入栈,
if(tokenPri === PRI_RIGHT){
// 将符号栈中元素依次弹出,则需要弹栈一直到左括号
while(PRI_TOKEN_MAP[opStack.peek] !== PRI_LEFT){
calcToken();
}
opStack.pop(); // 将左括号弹出,不放在数字栈里
} else {
// 将当前符号元素入栈
opStack.push(token);
}
} else { // 不是操作符就直接拼接在字符串上
str += token;
}
}
// 将符号栈中剩余的元素依次弹出
while(opStack.peek){
calcToken();
}
return str;
}
console.log(infixToPostfix('1+2*3+4')); // "123*+4+"
console.log(infixToPostfix('1 + 2 * (4 + 5 - 6) ')); // "1 2 4 5 + 6 - * +"
console.log(infixToPostfix('1 + 2 * (3 + (4 + 5 - 6) * 2)')); // "1 2 3 4 5 + 6 - 2 * + * +"
console.log(infixToPostfix('a + b * c + ( d * e + f ) * g')); // "a b c * + d e * f + g * +"
REFERENCE
参考文档
—END—