js解leetcode(67)-中等

本文详细介绍了JavaScript解LeetCode中的几个问题,包括所有可能的满二叉树的生成、子数组按位或操作的计算、RLE迭代器的实现、股票价格跨度的求解以及水果成篮的最大收集策略。通过具体题目解析,深入探讨了相关算法和数据结构的应用。
摘要由CSDN通过智能技术生成

1.所有可能的满二叉树

题目:

满二叉树是一类二叉树,其中每个结点恰好有 0 或 2 个子结点。

返回包含 N 个结点的所有可能满二叉树的列表。 答案的每个元素都是一个可能树的根结点。

答案中每个树的每个结点都必须有 node.val=0。

你可以按任何顺序返回树的最终列表。

思路:首先明确,满二叉树的节点数量是奇数。

然后,对于总数量为n的二叉树,根节点占一个节点,假设左节点有m个,那么右子树有n-1-m个。

所以对于数量为n的二叉树,可以枚举所有的子树节点,。

左子树1个节点,右子树是n-1-1,左子树3个节点,右子树n-1-3....

然后为了复用,用Map记录节点数量为n时的二叉树,对应的值是数组。

时间复杂度O(2^n),空间复杂度O(2^n)

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {number} N
 * @return {TreeNode[]}
 */
var allPossibleFBT = function (N) {
  if (!(N % 2)) return [];
  const map = new Map();
  map.set(1, [new TreeNode(0)]);
  const getNode = (n) => {
    if (map.has(n)) return map.get(n);
    if (n === 1) {
      return;
    } else {
      const res = [];
      for (let i = 1; i <= n - 2; i += 2) {
        const left = getNode(i);
        const right = getNode(n - 1 - i);
        const l1 = left.length;
        const l2 = right.length;
        for (let i = 0; i < l1; i++) {
          for (let j = 0; j < l2; j++) {
            const root = new TreeNode(0);
            root.left = left[i];
            root.right = right[j];
            res.push(root);
          }
        }
      }
      map.set(n, res);
      return res;
    }
  };
  return getNode(N);
};

2.子数组按位或操作

题目:

我们有一个非负整数数组 A。

对于每个(连续的)子数组 B = [A[i], A[i+1], ..., A[j]] ( i <= j),我们对 B 中的每个元素进行按位或操作,获得结果 A[i] | A[i+1] | ... | A[j]。

返回可能结果的数量。 (多次出现的结果在最终答案中仅计算一次。)

/**
 * @param {number[]} arr
 * @return {number}
 */
var subarrayBitwiseORs = function(arr) {
  const set1 = new Set();
  let set2 = new Set();
  set2.add(0);
  for (const x of arr) {
    const set3 = new Set();
    for (const y of set2) {
      set3.add(x | y);
    }
    set3.add(x);
    set2 = set3;
    for (const s of set2) {
      set1.add(s);
    }
  }
  return set1.size;
};

3.RLE迭代器

题目:

编写一个遍历游程编码序列的迭代器。

迭代器由 RLEIterator(int[] A) 初始化,其中 A 是某个序列的游程编码。更具体地,对于所有偶数 iA[i] 告诉我们在序列中重复非负整数值 A[i + 1] 的次数。

迭代器支持一个函数:next(int n),它耗尽接下来的  n 个元素(n >= 1)并返回以这种方式耗去的最后一个元素。如果没有剩余的元素可供耗尽,则  next 返回 -1 。

例如,我们以 A = [3,8,0,9,2,5] 开始,这是序列 [8,8,8,5,5] 的游程编码。这是因为该序列可以读作 “三个八,零个九,两个五”。

思路:如果用具体的数字填充,那么最后的结果是很大的,会内存溢出,所以不能这样,只能记录剩余的数字。

用栈保存每个数字和这个数字的长度,形成二维数组。在执行next时,判断栈顶的元素的长度是否大于等于n,如果大于等于,对应长度减去n,返回栈顶的元素的值。

如果小于,n减去栈顶元素的长度,并将栈顶元素弹出栈,进行下一轮循环

/**
 * @param {number[]} A
 */
var RLEIterator = function (A) {
  const l = A.length;
  this.data = [];
  for (let i = 0; i < l; i += 2) {
    this.data.push([A[i], A[i + 1]]);
  }
};

/**
 * @param {number} n
 * @return {number}
 */
RLEIterator.prototype.next = function (n) {
  while (n) {
    if (!this.data.length) return -1;
    const item = this.data.shift();
    if (item[0] >= n) {
      this.data.unshift([item[0] - n, item[1]]);
      n = 0;
      return item[1];
    } else {
      n -= item[0];
    }
  }
};

4.股票价格跨度

题目:

编写一个 StockSpanner 类,它收集某些股票的每日报价,并返回该股票当日价格的跨度。

今天股票价格的跨度被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。

例如,如果未来7天股票的价格是 [100, 80, 60, 70, 60, 75, 85],那么股票跨度将是 [1, 1, 1, 2, 1, 4, 6]。

思路:用单调递减栈记录比当前价格小的天数。

每次执行next时,判断栈顶价格和当前的价格大小,。当前价格更大,就弹出栈顶元素,并加上对应的长度;

所以用两个数组,一个数组记录价格,另一个数组记录天数

var StockSpanner = function () {
  this.data = [];
  this.length = [];
};

/**
 * @param {number} price
 * @return {number}
 */
StockSpanner.prototype.next = function (price) {
  let length = 1;
  while (this.data.length && this.data[this.data.length - 1] <= price) {
    this.data.pop();
    length += this.length.pop();
  }
  this.data.push(price);
  this.length.push(length);
  return length
};

5.水果成篮

题目:

在一排树中,第 i 棵树产生 tree[i] 型的水果。
你可以从你选择的任何树开始,然后重复执行以下步骤:

把这棵树上的水果放进你的篮子里。如果你做不到,就停下来。
移动到当前树右侧的下一棵树。如果右边没有树,就停下来。
请注意,在选择一颗树后,你没有任何选择:你必须执行步骤 1,然后执行步骤 2,然后返回步骤 1,然后执行步骤 2,依此类推,直至停止。

你有两个篮子,每个篮子可以携带任何数量的水果,但你希望每个篮子只携带一种类型的水果。

用这个程序你能收集的水果树的最大总量是多少?

思路:滑动窗口。在窗口中只能有两个元素,所以用一个set记录当前窗口的水果类型,用Left和right记录窗口的左右边界。right每次右移时u,判断当前窗口的成员是否超过2个,超过2个时,更新窗口的左边界。每次移动时,更新最大值

为了方便,用一个额外的变量记录上一个成员第一次出现的下标。这种方式也可以理解为三指针。

时间复杂度O(n),空间复杂度O(1)

/**
 * @param {number[]} tree
 * @return {number}
 */
var totalFruit = function(tree) {
  let max = 0;
  const l = tree.length;
  let left = 0,
    right = 0;
  let c = 0;
  const set = new Set();
  let last = 0;

  while (right < l) {
    if (set.size === 2 && !set.has(tree[right])) {
      set.clear();
      set.add(tree[right - 1]);
      left = last;
    }
    set.add(tree[right]);
    const curV = right - left + 1;
    max = Math.max(curV, max);
    if (tree[last] !== tree[right]) {
      last = right;
    }
    right++;
  }
  return max;
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值