js解leetcode(56)-中等

这篇博客主要解析了使用JavaScript解决LeetCode中的一些难题,包括森林中兔子的数量计算、字母大小写的全排列、判断二分图、求K站中转内最便宜的航班以及逃脱阻碍者的策略。每道题目的解题思路分别涉及数据结构和算法的应用,如深度遍历、排列组合等。
摘要由CSDN通过智能技术生成

1.森林中的兔子

题目:

森林中,每个兔子都有颜色。其中一些兔子(可能是全部)告诉你还有多少其他的兔子和自己有相同的颜色。我们将这些回答放在 answers 数组里。

返回森林中兔子的最少数量。

思路:原则上是,有同样数量其余颜色的兔子尽可能用同一种颜色,所以对于answer[i],用map记录之前出现过的answer的数量。如果之前出现过的数量等于当前的同颜色兔子数量,说明这种颜色已经被用完了,重置该数量对应的值为0;如果是第一次出现,则加上answer[i]+1(加一是因为要加上自己)
时间复杂度O(n) 空间复杂度O(n)

/**
 * @param {number[]} answers
 * @return {number}
 */
var numRabbits = function(answers) {
  const map = new Map();
  let c = 0;
  for (const n of answers) {
    const v = map.get(n) || 0;
    if (!v) {
      c += n + 1;
    }

    map.set(n, v === n ? 0 : v + 1);
  }
  return c;
};

2.字母大小写全排列

题目:给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。

思路:排列组合的一种方式。可以递归深度遍历也可以用栈广度遍历。遍历字符串,遇到字母,则新增两个分支,分别是当前字母的大写和小写,并拼接之前的结果,作为下一次的拼接值。

时间复杂度O(2^N),空间复杂度O(2^N) N是字母的数量

/**
 * @param {string} S
 * @return {string[]}
 */
var letterCasePermutation = function(S) {
  const res = [];
  const l = S.length;

  const findIndex = (pre) => {
    let index = l;
    for (let i = pre + 1; i < l; i++) {
      if (S[i].charCodeAt() > 60) {
        index = i;
        break;
      }
    }
    return index;
  };
  const index = findIndex(-1);
  if (index === l) return [S];
  const stack = [
    {
      index,
      left: S.slice(0, index),
    },
  ];
  while (stack.length) {
    const item = stack.shift();
    const index = findIndex(item.index);
    const v1 = `${item.left}${S[item.index].toLowerCase()}${S.slice(
      item.index + 1,
      index
    )}`;
    const v2 = `${item.left}${S[item.index].toUpperCase()}${S.slice(
      item.index + 1,
      index
    )}`;
    if (index === l) {
      res.push(v1, v2);
    } else {
      stack.push({ index, left: v1 }, { index, left: v2 });
    }
  }
  return res;
};

3.判断二分图

题目:

给定一个无向图graph,当这个图为二分图时返回true。

如果我们能将一个图的节点集合分割成两个独立的子集A和B,并使图中的每一条边的两个节点一个来自A集合,一个来自B集合,我们就将这个图称为二分图。

graph将会以邻接表方式给出,graph[i]表示图中与节点i相连的所有节点。每个节点都是一个在0到graph.length-1之间的整数。这图中没有自环和平行边: graph[i] 中不存在i,并且graph[i]中没有重复的值。

思路:深度递归处理。对于所有的节点i,从i=0开始遍历,节点的初始状态是0,遍历节点时,将节点状态变为1,并遍历和它相连的节点,状态是-1.如果遇到已经之前已经修改过状态的节点,判断之前的状态和当前的状态是否相同。相同的话停止当前遍历,不同则说明不符合条件,状态变为2做标识并停止遍历。最后判断所有节点的状态值是否有为2的

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

/**
 * @param {number[][]} graph
 * @return {boolean}
 */
var isBipartite = function(graph) {
  const l = graph.length;
  const list = new Array(l).fill(0);
  const set = new Set();
  const dfs = (i, v) => {
    set.add(i);
    if (list[i] !== 0 && list[i] !== v) {
      list[i] = 2;
      return;
    } else if (list[i] === v) {
      return;
    }
    list[i] = v;
    graph[i].forEach((item) => {
      dfs(item, -1 * v);
    });
  };
  for (let i = 0; i < l; i++) {
    if (set.has(i)) continue;
    dfs(i, 1);
  }
  return !list.some((item) => item === 2);
};

4.K站中转内最便宜的航班

题目:

有 n 个城市通过 m 个航班连接。每个航班都从城市 u 开始,以价格 w 抵达 v。

现在给定所有的城市和航班,以及出发城市 src 和目的地 dst,你的任务是找到从 src 到 dst 最多经过 k 站中转的最便宜的价格。 如果没有这样的路线,则输出 -1。

思路:深度遍历,记录到达每个城市在第k次中转时的价格,同样的中转次数时取较小值

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

/**
 * @param {number} n
 * @param {number[][]} flights
 * @param {number} src
 * @param {number} dst
 * @param {number} K
 * @return {number}
 */
var findCheapestPrice = function(n, flights, src, dst, K) {
  const map = new Map();
  for (const [start, end, v] of flights) {
    if (!map.has(start)) {
      map.set(start, new Map());
    }
    map.get(start).set(end, v);
  }
  const dp = new Array(n).fill("").map(() => new Array(K + 1).fill(Infinity));
  const dfs = (start, pre, k) => {
    if (k > K) return;
    const srcs = map.get(start);
    if(!srcs)return
    for (const [end, v] of srcs.entries()) {
      const cur = pre + v;
      if (dp[end][k] <= cur) continue;
      dp[end][k] = cur;
      if (end === dst) continue;
      dfs(end, cur, k + 1);
    }
  };
  dfs(src, 0, 0);
  const min = Math.min(...dp[dst]);
  return min === Infinity ? -1 : min;
};

5.逃脱阻碍者

题目:

你在进行一个简化版的吃豆人游戏。你从 [0, 0] 点开始出发,你的目的地是 target = [xtarget, ytarget] 。地图上有一些阻碍者,以数组 ghosts 给出,第 i 个阻碍者从 ghosts[i] = [xi, yi] 出发。所有输入均为 整数坐标 。

每一回合,你和阻碍者们可以同时向东,西,南,北四个方向移动,每次可以移动到距离原位置 1 个单位 的新位置。当然,也可以选择 不动 。所有动作 同时 发生。

如果你可以在任何阻碍者抓住你 之前 到达目的地(阻碍者可以采取任意行动方式),则被视为逃脱成功。如果你和阻碍者同时到达了一个位置(包括目的地)都不算是逃脱成功。

只有在你有可能成功逃脱时,输出 true ;否则,输出 false 。

思路:反向思路:只要有阻碍者先到达终点,就不可能赢。,所以比较绝对距离即可

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

/**
 * @param {number[][]} ghosts
 * @param {number[]} target
 * @return {boolean}
 */
var escapeGhosts = function(ghosts, target) {
  const distance = ghosts.map(
    (item) => Math.abs(item[0] - target[0]) + Math.abs(item[1] - target[1])
  );
  return distance.every(
    (item) => item > Math.abs(target[0]) + Math.abs(target[1])
  );
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值