1.骑士拨号器
题目:
国际象棋中的骑士可以按下图所示进行移动:
这一次,我们将 “骑士” 放在电话拨号盘的任意数字键(如上图所示)上,接下来,骑士将会跳 N-1 步。每一步必须是从一个数字键跳到另一个数字键。
每当它落在一个键上(包括骑士的初始位置),都会拨出键所对应的数字,总共按下 N 位数字。
你能用这种方式拨出多少个不同的号码?
因为答案可能很大,所以输出答案模 10^9 + 7。
思路:先观察键盘,然后可以记录每个键对应的下一个键的可能性。
然后用类似BFS的思路,记录每一步落在各个数字上的的可能性,然后以此计算下一步的各种的可能,用两个数组分别记录这一步和上一步的结果
时间复杂度O(N),空间复杂度O(1)
/**
* @param {number} n
* @return {number}
*/
var knightDialer = function(n) {
const map = new Map();
const mod = Math.pow(10, 9) + 7;
map.set(1, [6, 8]);
map.set(2, [7, 9]);
map.set(3, [4, 8]);
map.set(4, [0, 3, 9]);
map.set(5, []);
map.set(6, [0, 1, 7]);
map.set(7, [2, 6]);
map.set(8, [1, 3]);
map.set(9, [2, 4]);
map.set(0, [4, 6]);
const dp1 = new Array(10).fill(1);
const dp2 = new Array(10).fill(0);
while (--n) {
for (let i = 0; i < 10; i++) {
map.get(i).forEach((item) => {
dp2[item] = (dp2[item] + dp1[i]) % mod;
});
}
for (let i = 0; i < 10; i++) {
dp1[i] = dp2[i];
dp2[i] = 0;
}
}
return dp1.reduce((a, b) => (a + b) % mod, 0);
};
2.最小面积矩形
题目:
给定在 xy 平面上的一组点,确定由这些点组成的矩形的最小面积,其中矩形的边平行于 x 轴和 y 轴。
如果没有任何矩形,就返回 0。
思路:双重遍历所有的点,如果这两个点在x/y轴的坐标值都不同,那么判断对应另外两个端点的坐标是否在数组内,在的话计算矩形面积并更新最大值
时间复杂度O(n2),空间复杂度O(1)
/**
* @param {number[][]} points
* @return {number}
*/
var minAreaRect = function(points) {
const set = new Set(points.map((item) => `${item[0]}-${item[1]}`));
const l = points.length;
let min = Infinity;
for (let i = 0; i < l - 1; i++) {
for (let j = i + 1; j < l; j++) {
const leftup = points[i];
const rightdown = points[j];
if (leftup[0] !== rightdown[0] && leftup[1] !== rightdown[1]) {
if (
set.has(`${leftup[0]}-${rightdown[1]}`) &&
set.has(`${rightdown[0]}-${leftup[1]}`)
) {
const cur =
Math.abs(leftup[0] - rightdown[0]) *
Math.abs(leftup[1] - rightdown[1]);
min = Math.min(min, cur);
}
}
}
}
return min === Infinity ? 0 : min;
};
3.使数组唯一的最小增量
题目:
给定整数数组 A,每次 move 操作将会选择任意 A[i]
,并将其递增 1
。
返回使 A
中的每个值都是唯一的最少操作次数。
思路:先对数组排序,然后按照贪心的思想,从第二个元素开始,最多时其比前一个元素大1.
时间复杂度O(nlogn),空间复杂度O(logn)
/**
* @param {number[]} A
* @return {number}
*/
var minIncrementForUnique = function(A) {
A.sort((a, b) => a - b);
let c = 0;
const l = A.length;
for (let i = 1; i < l; i++) {
if (A[i] <= A[i - 1]) {
c += A[i - 1] + 1 - A[i];
A[i] = A[i - 1] + 1;
}
}
return c;
};
4.验证栈序列
题目:
给定 pushed 和 popped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false 。
思路:过程模拟。用一个指针指向poped的第一个元素,pushed的元素肯定是从前往后推入的所以用一个栈将pushed的元素依次入栈,遇到和poped当前元素相同的时候,说明该元素出栈了,poped的指针右移,pushed的该元素删除,然后进行下一轮比较
时间复杂度O(n),空间复杂度O(n)
/**
* @param {number[]} pushed
* @param {number[]} popped
* @return {boolean}
*/
var validateStackSequences = function(pushed, popped) {
const l = pushed.length;
const stack = [];
let j = 0;
for (const n of pushed) {
stack.push(n);
while (stack.length && j < l && stack[stack.length - 1] == popped[j]) {
stack.pop();
j++;
}
}
return j === l;
};
5.移除最多的同行或者同列石头
题目:
n 块石头放置在二维平面中的一些整数坐标点上。每个坐标点上最多只能有一块石头。
如果一块石头的 同行或者同列 上有其他石头存在,那么就可以移除这块石头。
思路:典型的并查集。并且是两个方向上的查找,所以用双重循环枚举所有可能。
用一个数组记录每个石头对应的原始堆,如果它和另一个石头有交叉,那么把另一石头的原始堆指向自己的原始堆。
数组初始值为自身下标,每个不同即可
最后,数组中原始堆更改的石头,说明可以移动
时间复杂度O(n2),空间复杂度O(n)
/**
* @param {number[][]} stones
* @return {number}
*/
var removeStones = function (stones) {
const l = stones.length;
const parent = new Array(l).fill(0).map((value, index) => index);
//合并同行同列的点 指向同一根节点
for (let i = 0; i < l; i++) {
for (let j = i + 1; j < l; j++) {
if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1]) {
union(parent, i, j);
}
}
}
return parent.filter((item, index) => item !== index).length;
};
const union = (parent, i, j) => {
parent[find(parent, j)] = find(parent, i);
};
const find = (parent, x) => {
if (parent[x] != x) {
parent[x] = find(parent, parent[x]);
}
return parent[x];
};