js解leetcode(77)-中等

本文详细介绍了使用JavaScript解决LeetCode中5道不同类型的题目,包括腐烂的橘子的BFS解法、最大二叉树的构建、检查替换后词的有效性、求最大连续1子数组和笨阶乘的计算策略。每个问题都提供了思路解析和时间复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.腐烂的橘子

题目:

在给定的网格中,每个单元格可以有以下三个值之一:

值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。

返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1。

思路:bfs。先记录所有腐烂的橘子的位置,以及新鲜橘子的数量n。

然后从所有腐烂的橘子位置开始,每次往外扩散1个单位,每次扩散时遇到新鲜橘子才能继续扩散,否则停止。遇到新鲜橘子(1),将它置为腐烂的橘子(2),同时n-1.每一轮计数1

遇到所有可能腐烂的橘子之后,判断n是否为0,不为0说明有一些没腐烂。否则返回计数的时间

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

/**
 * @param {number[][]} grid
 * @return {number}
 */
var orangesRotting = function(grid) {
  const h = grid.length;
  const w = grid[0].length;
  let c = 0;
  let node = [];
  const inRange = (i, j) => {
    return i >= 0 && i < h && j >= 0 && j < w;
  };
  let n = 0;
  const xv = [1, -1, 0, 0];
  const yv = [0, 0, 1, -1];
  for (let i = 0; i < h; i++) {
    for (let j = 0; j < w; j++) {
      if (grid[i][j] === 2) {
        node.push([i, j]);
      } else if (grid[i][j] === 1) {
        n++;
      }
    }
  }
  while (node.length) {
    const temp = node.reduce((sum, item) => {
      for (let x = 0; x < 4; x++) {
        if (
          inRange(item[0] + xv[x], item[1] + yv[x]) &&
          grid[item[0] + xv[x]][item[1] + yv[x]] === 1
        ) {
          sum.push([item[0] + xv[x], item[1] + yv[x]]);
          grid[item[0] + xv[x]][item[1] + yv[x]] = 2;
          n--;
        }
      }
      return sum;
    }, []);
    node = temp;
    if (!node.length) break;
    c++;
  }
  return n ? -1 : c;
};

2.最大二叉树

题目:

最大树定义:一个树,其中每个节点的值都大于其子树中的任何其他值。

给出最大树的根节点 root。

就像之前的问题那样,给定的树是从列表 A(root = Construct(A))递归地使用下述 Construct(A) 例程构造的:

如果 A 为空,返回 null
否则,令 A[i] 作为 A 的最大元素。创建一个值为 A[i] 的根节点 root
root 的左子树将被构建为 Construct([A[0], A[1], ..., A[i-1]])
root 的右子树将被构建为 Construct([A[i+1], A[i+2], ..., A[A.length - 1]])
返回 root
请注意,我们没有直接给定 A,只有一个根节点 root = Construct(A).

假设 B 是 A 的副本,并在末尾附加值 val。题目数据保证 B 中的值是不同的。

返回 Construct(B)。

思路:如果根节点的值比给定值小,说明应该以给定的值作为新的跟节点。

否则,该节点应该放在右侧(因为新节点在数组的右半部分)

所以递归处理右侧子树,并返回替换之后的新子树

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

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} val
 * @return {TreeNode}
 */
var insertIntoMaxTree = function(root, val) {
      if (!root) return new TreeNode(val);
  if (root.val < val) {
    const node = new TreeNode(val);
    node.left = root;
    return node;
  }
  root.right = insertIntoMaxTree(root.right, val);
  return root;
};

3.检查替换后的词是否有效

题目:

给你一个字符串 s ,请你判断它是否 有效 。
字符串 s 有效 需要满足:假设开始有一个空字符串 t = "" ,你可以执行 任意次 下述操作将 t 转换为 s :

将字符串 "abc" 插入到 t 中的任意位置。形式上,t 变为 tleft + "abc" + tright,其中 t == tleft + tright 。注意,tleft 和 tright 可能为 空 。
如果字符串 s 有效,则返回 true;否则,返回 false。

思路:和有效括号类似,用单调栈处理。每次遇到字符,如果和栈顶两个字符能组成abc,则删除栈顶两个元素,否则将当前元素入栈,

最后如果栈为空,说明有效,否则无效。

当然,可以提前排除一些特殊情况,比如c在b前面,b在a前面等

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

/**
 * @param {string} s
 * @return {boolean}
 */
var isValid = function(s) {
  const stack = [];
  for (const n of s) {
    const l = stack.length;
    if (n === "c" && stack[l - 1] === "b" && stack[l - 2] === "a") {
      stack.pop();
      stack.pop();
    } else {
      stack.push(n);
    }
  }
  return !stack.length;
};

4.最大连续1的个数III

题目:

给定一个由若干 0 和 1 组成的数组 A,我们最多可以将 K 个值从 0 变成 1 。

返回仅包含 1 的最长(连续)子数组的长度。

思路:滑动窗口,或者说双指针。右指针每向右移动一个位置,左指针向右移动,使得左右指针范围内的0的数量不大于K个。

为了方便计算0的数量,可以用前缀和数组先计算出每个下标处的累计的0的数量

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

/**
 * @param {number[]} A
 * @param {number} K
 * @return {number}
 */
var longestOnes = function(A, K) {
  const l = A.length;

  let left = 0;
  let right = 0;
  let max = 0;
  const sumLeft = new Array(l).fill(0);
  sumLeft[0] = A[0] === 1 ? 0 : 1;
  for (let i = 1; i < l; i++) {
    sumLeft[i] = (sumLeft[i - 1] || 0) + (A[i] === 1 ? 0 : 1);
  }
  while (right < l) {
    while (sumLeft[right] - (sumLeft[left - 1] || 0) > K && left < right) {
      left++;
    }
    max = Math.max(max, right - left + 1);
    right++;
  }
  if (!K && max === 1) return 0;
  return max;
};

5.笨阶乘

题目:

通常,正整数 n 的阶乘是所有小于或等于 n 的正整数的乘积。例如,factorial(10) = 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1。

相反,我们设计了一个笨阶乘 clumsy:在整数的递减序列中,我们以一个固定顺序的操作符序列来依次替换原有的乘法操作符:乘法(*),除法(/),加法(+)和减法(-)。

例如,clumsy(10) = 10 * 9 / 8 + 7 - 6 * 5 / 4 + 3 - 2 * 1。然而,这些运算仍然使用通常的算术运算顺序:我们在任何加、减步骤之前执行所有的乘法和除法步骤,并且按从左到右处理乘法和除法步骤。

另外,我们使用的除法是地板除法(floor division),所以 10 * 9 / 8 等于 11。这保证结果是一个整数。

实现上面定义的笨函数:给定一个整数 N,它返回 N 的笨阶乘。

思路:仔细观察,是有规律的。

先判断N是否大于等于4,如果小于的话,直接返回、

然后先计算初始值。前4个数是~~(N * (N-1)/(N-2)) + N - 3.,然后后面每4个数都是 - ~~(N * (N-1)/(N-2)) + N - 3.

所以后续以4为梯度,累计结果,最后再计算小于4时对应需要处理的数

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

/**
 * @param {number} N
 * @return {number}
 */
var clumsy = function(N) {
  if (N === 1) {
    return 1;
  } else if (N === 2) {
    return 2;
  } else if (N === 3) {
    return 6;
  }
  let init = ~~((N * (N - 1)) / (N - 2)) + N - 3;
  N -= 4;
  while (N >= 4) {
    init -= ~~((N * (N - 1)) / (N - 2));
    init += N - 3;
    N -= 4;
  }
  if (N === 1) {
    init -= 1;
  } else if (N === 2) {
    init -= 2;
  } else if (N == 3) {
    init -= 6;
  }
  return init;
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值