# 栈和队列

栈和队列值允许在线性集合的一端进行操作,推入和弹出操作。栈先进后出,队列先进先出,这就是他们最大的不同点。

# 栈Stack

Stack是一种先进后出的数据结构, 在js中,我们可以使用Array或者链表来简单的模拟一个栈, 或者也可以像下面这样子简单编写一个栈的构造函数

class Stack {
  constructor() {
    this.stack = [];
  }
  push(item) {
    this.stack.push(item);
  }
  pop() {
    return this.stack.pop();
  }
  peek() {
    if (this.stack && this.stack.length > 0) {
      return this.stack[this.stack.length - 1];
    }
    return undefined;
  }
  length() {
    return this.stack.length;
  }
  isEmpty() {
    return this.stack.length === 0;
  }
  clear() {
    this.stack.length = 0;
  }
}

# 栈的典型基础案例

# 1.进制转换

比如十进制转八进制

// 十进制转换为八进制
function decimal2octal(num) {
  if (typeof num !== 'number') {
    throw new TypeError('Expect a number input');
  }
  if (num === 0) return 0;
  let quotient = num;
  let remainder;
  const stack = [];
  // 只要商大于0 就把余数推到队列
  while (quotient > 0) {
    remainder = quotient % 8;
    stack.push(remainder);
    quotient = Math.floor(quotient / 8);
  }
  // 最后反转即可
  return stack.reverse().join('') - 0;
}

# 2.括号匹配检验

遇到左括号就进栈,遇到右括号就跟当前栈顶匹配,能匹配就将其出栈,否则不匹配.

比如 (()), ()()()等就是合法闭合的, 如果是多种括号,比如([{}])() 思路也是一样的

function isClosure2(str) {
  if (!str) return false;
  const stack = [];
  if (str[0] === '(') {
    stack.push(str[0]);
  } else {
    return false;
  }
  for (let i = 1, len = str.length; i < len; i++) {
    const char = str[i];
    if (char === '(') {
      stack.push(char);
    }
    if (char === ')') {
      stack.pop();
    }
  }
  return stack.length === 0;
}

# 3.中缀表达式的计算机求值

比如求算 ”1 + 3 * 4 / 2“要求算出7

1、设置两个栈,一个数字栈numStack,用于存储表达式中涉及到的数字,operatorStack用于存储表达式中涉及到的运算符 逐个字符分析表达式,直到全部字符都已分析完 2、若当前字符为数字,则判断是否后续字符也为数字,若为数字则进行拼接,直到下一个数字为运算符为止,此时将拼接好的多位数字压入数字栈中。(如果已经是最后一个字符则直接压入栈) 3、若当前字符为算数运算符 如果运算符栈为空则直接压入栈中 运算符不为空,则对运算符优先级进行判断 如果当前运算符优先级大于等于栈顶运算符则直接压入栈中 如果优先级低于栈顶运算符,则,从数字栈中取出两个数据,将当前栈顶运算符弹出进行运算,将结果压入数字栈中,将当前运算符压入运算符栈中。 4、此时数字与运算符都已经压入栈中,此时运算符栈中均为优先级相同的运算符,需要进行收尾操作,如果运算符栈不为空,则依次从数字栈中弹出两个数据,与当前栈顶的运算符进行运算。将结果压入数字栈中。最后数字栈中的数字就是所要求解的结果

下面的代码可能不是很正确, 但是可以通过大部分的表达式, 暂时没有考虑括号的情况

class Stack {
  constructor() {
    this.stack = [];
  }
  push(item) {
    this.stack.push(item);
  }
  pop() {
    return this.stack.pop();
  }
  peek() {
    if (this.stack && this.stack.length > 0) {
      return this.stack[this.stack.length - 1];
    }
    return undefined;
  }
  length() {
    return this.stack.length;
  }
  isEmpty() {
    return this.stack.length === 0;
  }
  clear() {
    this.stack.length = 0;
  }
}
/* 113 + 20 * 6 / 3 */

// 判断是否是一个数字
function isNumChar(char) {
  const numReg = /^\d{1}$/;
  return numReg.test(char);
}

// 判断是否是操作符
function isOperatorChar(char) {
  const operatorReg = /^\+|\-|\*|\/|\(|\)$/;
  return operatorReg.test(char);
}

function calculate(num1, num2, operator) {
  switch (operator) {
    case '+':
      return num1 + num2;
    case '-':
      return num1 - num2;
    case '*':
      return num1 * num2;
    case '/':
      return num1 / num2;

    default:
      break;
  }
}

function calExpression(expression) {
  const numStack = new Stack();
  const operatorStack = new Stack();
  let n = 0,
    i = 0,
    len = expression.length;
  for (; i < len; i++) {
    const char = expression[i];
    if (char === ' ') {
      // 过滤一下空格
      continue;
    }
    if (isNumChar(char)) {
      n = parseInt(char);
      while (expression[i + 1] && expression[i + 1] !== ' ') {
        if (isNumChar(expression[i + 1])) {
          n = n * 10 + (expression[i + 1] - 0);
          i++;
        } else {
          break;
        }
      }
      numStack.push(n);
    } else if (isOperatorChar(char)) {
      //   operatorStack.push(char);
      switch (char) {
        case '+':
          if (operatorStack.length() === 0) {
            operatorStack.push(char);
          } else {
            const peek = operatorStack.peek();
            if (peek === '+' || peek === '-' || peek === '(') {
              operatorStack.push(char);
            } else {
              // 需要计算
              const num2 = numStack.pop();
              const num1 = numStack.pop();
              const operator = operatorStack.pop();
              const result = calculate(num1, num2, operator);
              numStack.push(result);
              operatorStack.push(char);
            }
          }
          break;
        case '-':
          if (operatorStack.length() === 0) {
            operatorStack.push(char);
          } else {
            const peek = operatorStack.peek();
            if (peek === '+' || peek === '-' || peek === '(') {
              operatorStack.push(char);
            } else {
              // 需要计算
              const num2 = numStack.pop();
              const num1 = numStack.pop();
              const operator = operatorStack.pop();
              const result = calculate(num1, num2, operator);
              numStack.push(result);
              operatorStack.push(char);
            }
          }
          break;
        case '*':
        case '/':
          if (operatorStack.length() > 0) {
            const peek = operatorStack.peek();
            if (peek === '*' || peek === '/') {
              const num2 = numStack.pop();
              const num1 = numStack.pop();
              const operator = operatorStack.pop();
              const result = calculate(num1, num2, operator);
              numStack.push(result);
              //   operatorStack.push(char);
            }
          }
          operatorStack.push(char);
          break;
        case '(':
          operatorStack.push(char);
          break;
        case ')':
          while (operatorStack.peek() !== '(') {
            const num2 = numStack.pop();
            const num1 = numStack.pop();
            const operator = operatorStack.pop();
            const result = calculate(num1, num2, operator);
            numStack.push(result);
          }
          // 弹出(
          operatorStack.pop();
          break;

        default:
          break;
      }
    }
  }
  while (operatorStack.length() > 0) {
    const num2 = numStack.pop();
    const num1 = numStack.pop();
    const operator = operatorStack.pop();
    const result = calculate(num1, num2, operator);
    numStack.push(result);
  }
  // console.log(numStack, operatorStack);
  return numStack.pop();
}

// 下面的测试用例一般都可以通过
console.log(calExpression('113 + 20 * 6 / 3 - 0'));
console.log(calExpression('1 + 2'));
console.log(calExpression('1 * 2 + 3'));
console.log(calExpression('1 + 2 * 3'));
console.log(calExpression('1 + 2 * 3 / 6'));
console.log(calExpression('1 + 2 * 3 / 4'));
console.log(calExpression('(1 + 2 + 3) * 5'));
console.log(calExpression('(1 + 2 - 1) * 5 * 6'));

还有一些变种题目, 比如波兰表达式求值, 逆波兰表达式求值,给优先级高的加上括号等题目,都可以采用栈来解决

# 队列 Queue

队列是尾部插入,头部弹出的先进先出的数据结构

# 队列案例

# 1.循环队列

6个孩子围城一个圈,排列顺序孩子们自己指定。第一个孩子手里有一个烫手的山芋,需要在计时器计时1秒后将山芋传递给下一个孩子,依次类推。规则是,在计时器每计时7秒时,手里有山芋的孩子退出游戏。该游戏直到剩下一个孩子时结束,最后剩下的孩子获胜。请使用队列实现该游戏策略,排在第几个位置最终会获胜。

function getWinner(nameList, num) {
  const queue = [...nameList];
  while (queue.length > 1) {
    for (let i = 0; i < num; i++) {
      queue.push(queue.shift());
    }
    queue.shift();
  }
  return queue.shift();
}

其他按理后续补吧

上次更新: 1/22/2025, 9:39:13 AM