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;
};