算法-队列 & 栈

47 阅读3分钟

队列

一.定义

队列(queue)是一种先进先出的线性表,简称FIFO。

在表一端(表尾)插入 push,在另一端(表头)删除 shift。

和栈相反 先进先出的一个数据结构

按照常识理解就是银行排号办理业务, 先去领号排队的人, 先办理业务

二.特点

1)在表一端(表尾)插入,在另一端(表头)删除的线性表。

2)表的插入操作称为入队,表的取出操作称为出队。

3)队列的存储结构分为顺序存储或链式存储,顺序存储称为顺序队,链式存储称为链队。以循环顺序队列更常见。

4)队列分为两种:双向队列和单向队列 。

单向队列(队列):只能在一端删除数据,另一端插入数据。

双向队列(双端队列):两端都可以进行插入数据和删除数据操作。

js中没有队列的数据类型,但我们可以通过 Array来模拟一个

const queue = [];

// 入队
queue.push(1);
queue.push(2);

// 出队
const first = queue.shift();
const end = queue.shift();

最近的请求次数

var RecentCounter = function () {
  // 初始化队列
  this.q = [];
};

// 输入 inputs = [[],[1],[100],[3001],[3002]] 请求间隔为 3000ms
// 输出 outputs = [null,1,2,3,3]   

// 时间复杂度 O(n) n为剔出老请求的长度
// 空间复杂度 O(n) n为最近请求的次数
RecentCounter.prototype.ping = function (t) {
  // 如果传入的时间小于等于最近请求的时间,则直接返回0
  if (!t) return null

  // 将传入的时间放入队列
  this.q.push(t);

  // 如果队头小于 t - 3000 则剔除队头
  while (this.q[0] < t - 3000) {
    this.q.shift();
  }

  // 返回最近请求的次数
  return this.q.length;
};

html 节点 实现 队列 广度优先

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div class="parent">
    <div class="child-1">
      <div class="child-1-1">
        <div class="child-1-1-1">
          a
        </div>
      </div>
      <div class="child-1-2">
        <div class="child-1-2-1">
          b
        </div>
      </div>
      <div class="child-1-3">
        c
      </div>
    </div>

    <div class="child-2">
      <div class="child-2-1">
        d
      </div>
      <div class="child-2-2">
        e
      </div>
    </div>
    <div class="child-3">
      <div class="child-3-1">
        f
      </div>
    </div>

  </div>

</body>
<script>
  const el = document.querySelector('.parent')
  // 广度优先
  let widthTraversal2 = (node) => {
    let nodes = []
    let stack = []
    if (node) {
      stack.push(node)
      while (stack.length) {
        let item = stack.shift()
        let children = item.children
        nodes.push(item)
        // 队列,先进先出
        // nodes = [] stack = [parent]
        // nodes = [parent] stack = [child1,child2,child3]
        // nodes = [parent, child1] stack = [child2,child3,child1-1,child1-2]
        // nodes = [parent,child1,child2]
        for (let i = 0; i < children.length; i++) {
          stack.push(children[i])
        }
      }
    }
    return nodes
  }

  console.log(widthTraversal2(el));

</script>

</html>

栈的定义

一个后进先出的数据结构

按照常识理解就是有序的挤公交,最后上车的人会在门口,然后门口的人会最先下车

栈(stack)是限定仅在表尾进行插入或者删除的线性表。对于栈来说,表尾端称为栈顶(top),表头端称为栈低(bottom)。不含元素的空表称为空栈。因为栈限定在表尾进行插入或者删除,所以栈又被称为后进先出的线性表(简称LIFO:Last in, First out.结构)。

栈的插入操作:叫做进栈,也叫作压栈,入栈。 push

栈的删除操作:叫做出栈,也叫弹栈。 pop

js中没有栈的数据类型,但我们可以通过Array来模拟一个

const stack = [];
stack.push(1); // 入栈
stack.push(2); // 入栈
const item1 = stack.pop(2);  //出栈的元素

十进制转二进制

// 时间复杂度 O(n) n为二进制的长度
// 空间复杂度 O(n) n为二进制的长度
const dec2bin = (dec) => {
  // 创建一个字符串
  let res = "";

  // 创建一个栈
  let stack = []

  // 遍历数字 如果大于0 就可以继续转换2进制
  while (dec > 0) {
    // 将数字的余数入栈
    stack.push(dec % 2);

    // 除以2
    dec = dec >> 1;
  }

  // 取出栈中的数字
  while (stack.length) {
    res += stack.pop();
  }

  // 返回这个字符串
  return res;
};

判断字符串的有效括号

// 时间复杂度O(n) n为s的length
// 空间复杂度O(n)
const isValid = (s) => {

  // 如果长度不等于2的倍数肯定不是一个有效的括号
  if (s.length % 2 === 1) return false;

  // 创建一个栈
  let stack = [];

  // 遍历字符串
  for (let i = 0; i < s.length; i++) {

    const c = s[i];

    // 如果是左括号就入栈
    if (c === '(' || c === "{" || c === "[") {
      stack.push(c);
    } else {

      // 如果不是左括号 且栈为空 肯定不是一个有效的括号 返回false
      if (!stack.length) return false

      // 拿到最后一个左括号
      const top = stack[stack.length - 1];

      // 如果是右括号和左括号能匹配就出栈
      if ((top === "(" && c === ")") || (top === "{" && c === "}") || (top === "[" && c === "]")) {
        stack.pop();
      } else {

        // 否则就不是一个有效的括号
        return false
      }
    }

  }
  return stack.length === 0;
};

html 节点 实现栈 深度优先

<!DOCTYPE html>
<html lang="en">
  
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
  </head>
  
  <body>
    <div class="parent">
      <div class="child-1">
        <div class="child-1-1">
          <div class="child-1-1-1">
            a
          </div>
        </div>
        <div class="child-1-2">
          <div class="child-1-2-1">
            b
          </div>
        </div>
        <div class="child-1-3">
          c
        </div>
      </div>
      
      <div class="child-2">
        <div class="child-2-1">
          d
        </div>
        <div class="child-2-2">
          e
        </div>
      </div>
      <div class="child-3">
        <div class="child-3-1">
          f
        </div>
      </div>
      
    </div>
    
  </body>
  <script>
    const el = document.querySelector('.parent')
    // 深度优先
    let deepTraversal1 = (node) => {
      let nodes = []
      // 栈
      let stack = []
      if (node) {
        // 栈中 push 一个跟节点
        stack.push(node)
        // 如果栈有值
        while (stack.length) {
          // 拿到栈的最后一个值
          let item = stack.pop()
          // 最后一个值的子节点
          let children = item.children
          // push 到 nodes
          nodes.push(item)
          // 先进后出
          // 倒叙遍历 注意::::  因为是要深度优先 就先把有所当前的倒叙进入
          // 第一个 parent
          // 第二个 child-1
          //  nodes = [parent] chilren = [child3,child2,child1]
          //  nodes = [parent,child1] chilren = [child3,child2]
          //  nodes = [parent,child1] chilren = [child3,child2,child1-1,child1-1-1,...]
          for (let i = children.length - 1; i >= 0; i--) {
            // 把每一项添加到数据里面 最后 然后在通过 pop 拿到
            stack.push(children[i])
          }
        }
      }
      return nodes
    }
    console.log(deepTraversal1(el));
    
    
  </script>
  
</html>