队列是一种常用的数据结构,它最大的特点是先入先出,即先进入队列中的元素最先出来。

  1. 滑动窗口的平均值
请实现如下类型MovingAverage,计算滑动窗口中所有数字的平均值,该类型构造函数的参数确定滑动窗口的大小,每次调用成员函数next时都会在滑动窗口中添加一个整数,并返回滑动窗口中所有数字的平均值。
/**
 * Initialize your data structure here.
 * @param {number} size
 */
var MovingAverage = function(size) {
    this.size = size;
    this.arr = []
    this.sum = 0
};

/** 
 * @param {number} val
 * @return {number}
 */
MovingAverage.prototype.next = function(val) {
    this.arr.push(val)
    this.sum += val
    if(this.arr.length>this.size) {
        this.sum -= this.arr.shift()
    }
    return this.sum / this.arr.length
};
  1. 最近请求次数
请实现如下类型RecentCounter,它是统计过去3000ms内的请求次数的计数器。该类型的构造函数RecentCounter初始化计数器,请求数初始化为0;函数ping(int t)在时间t添加一个新请求(t表示以毫秒为单位的时间),并返回过去3000ms内(时间范围为[t-3000,t])发生的所有请求数。假设每次调用函数ping的参数t都比之前调用的参数值大。
var RecentCounter = function() {
    this.arr = []
};

/** 
 * @param {number} t
 * @return {number}
 */
RecentCounter.prototype.ping = function(t) {
    this.arr.push(t)
    while(this.arr.length > 1 && this.arr[0] + 3000 < t) {
        this.arr.shift()
    }
    return this.arr.length
};

二叉树的广度优先遍历

应聘者在面试时经常需要使用队列来解决与广度优先搜索相关的问题。图的广度优先遍历请看图的章节。

二叉树的广度优先搜索是从上到下按层遍历二叉树,从二叉树的根节点开始,先遍历二叉树的第1层,再遍历第2层,接着遍历第3层,并以此类推。

通常基于队列来实现二叉树的广度优先搜索。从二叉树的根节点开始,先把根节点放入一个队列之中,然后每次从队列中取出一个节点遍历。如果该节点有左右子节点,则分别将它们添加到队列当中。重复这个过程,直到所有节点都遍历完为止,此时队列为空。实现二叉树广度优先搜索的经典代码如下所示:

  1. 在完全二叉树中添加节点
在完全二叉树中,除最后一层之外其他层的节点都是满的(第n层有2n-1个节点)。最后一层的节点可能不满,该层所有的节点尽可能向左边靠拢

要理解完全二叉树的定义:除了最后一层节点不满,其他层节点都是满的

/**
 * @param {TreeNode} root
 */
var CBTInserter = function(root) {
    if(root == null) return [];
    this.root = root;
    this.queue = [];
    this.queue.push(root)
    while(this.queue[0].left&&this.queue[0].right) {
        const node = this.queue.shift();
        this.queue.push(node.left)
        this.queue.push(node.right)
    }   
};

/** 
 * @param {number} v
 * @return {number}
 */
CBTInserter.prototype.insert = function(v) {
    const parent = this.queue[0]
    const child = new TreeNode(v)
    if(parent.left == null) {
        parent.left = child
    } else {
        parent.right = child;
        this.queue.shift()
        this.queue.push(parent.left)
        this.queue.push(parent.right)
    }
    return parent.val;
};

/**
 * @return {TreeNode}
 */
CBTInserter.prototype.get_root = function() {
    return this.root
};

最后分析上述代码的效率。类型CBTInserter的构造函数从本质上来说是按照广度优先搜索的顺序找出二叉树中所有既有左子节点又有右子节点的节点,因此时间复杂度是O(n)。调用函数insert在完全二叉树中每添加一个节点最多只需要在队列中删除一个节点并添加两个节点。通常,队列的插入、删除操作的时间复杂度都是O(1),因此函数insert的时间复杂度是O(1)。显然,函数get_root的时间复杂度是O(1)。

  1. 二叉树中每层的最大值
输入一棵二叉树,请找出二叉树中每层的最大值

关键就在于遍历层的时候,知道什么时候层开始,什么时候层结束。1个办法就是计数。另一个办法是利用2个队列实现广度优先遍历


/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var largestValues = function(root) {
  let current = 0;
  let next = 0;
  const queue = [];
  if(root != null) {
    queue.push(root)
    current = 1
  }

  const result = [] // 结果
  let max = -Infinity;
  while(queue.length) {
    const node = queue.shift();
    current--
    max = Math.max(max, node.val)

    if(node.left != null) {
      queue.push(node.left)
      next++
    }

    if(node.right != null) {
      queue.push(node.right)
      next++
    }

    if(current === 0) {
      result.push(max)
      current = next
      max = -Infinity
      next = 0
    }
  }
  return result
};

之前的想太复杂了,没必要2**n,

  • 双队列实现广度优先搜索
  1. 二叉树最低层最左边的值
给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

/**
 * @param {TreeNode} root
 * @return {number}
 */
var findBottomLeftValue = function(root) {
    let queue = []
    let next_queue = []
    queue.push(root)
    let temp = []
    while(queue.length) {
        const node = queue.shift()
        temp.push(node)
        if(node.left) {
            next_queue.push(node.left)
        }
        if(node.right) {
            next_queue.push(node.right)
        }

        if(queue.length == 0 && next_queue.length) {
            queue = next_queue
            temp = []
            next_queue = []
        }
    }
    return temp[0].val
};
  1. 二叉树的右侧视图
如何在一棵二叉树中找出它最低层最左边节点的值?假设二叉树中最少有一个节点。例如,在如图7.5所示的二叉树中最低层最左边一个节点的值是5。
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var rightSideView = function(root) {
    if(root == null) return []
    let queue = []
    let next_queue = []
    queue.push(root)
    let temp = []
    const result = []
    while(queue.length) {
        const node = queue.shift()
        temp.push(node)
        if(node.left) {
            next_queue.push(node.left)
        }
        if(node.right) {
            next_queue.push(node.right)
        }

        if(queue.length == 0 && next_queue.length) {
            queue = next_queue
            result.push(temp.pop().val)
            temp = []
            next_queue = []
        }
    }
    if(temp.length) {
        result.push(temp.pop().val)
    } 
    return result;
};

看见了
876 声望16 粉丝

前端开发,略懂后台;