1.元音拼写检查器
题目:
在给定单词列表 wordlist 的情况下,我们希望实现一个拼写检查器,将查询单词转换为正确的单词。
对于给定的查询单词 query,拼写检查器将会处理两类拼写错误:
大小写:如果查询匹配单词列表中的某个单词(不区分大小写),则返回的正确单词与单词列表中的大小写相同。
例如:wordlist = ["yellow"], query = "YellOw": correct = "yellow"
例如:wordlist = ["Yellow"], query = "yellow": correct = "Yellow"
例如:wordlist = ["yellow"], query = "yellow": correct = "yellow"
元音错误:如果在将查询单词中的元音(‘a’、‘e’、‘i’、‘o’、‘u’)分别替换为任何元音后,能与单词列表中的单词匹配(不区分大小写),则返回的正确单词与单词列表中的匹配项大小写相同。
例如:wordlist = ["YellOw"], query = "yollow": correct = "YellOw"
例如:wordlist = ["YellOw"], query = "yeellow": correct = "" (无匹配项)
例如:wordlist = ["YellOw"], query = "yllw": correct = "" (无匹配项)
此外,拼写检查器还按照以下优先级规则操作:
当查询完全匹配单词列表中的某个单词(区分大小写)时,应返回相同的单词。
当查询匹配到大小写问题的单词时,您应该返回单词列表中的第一个这样的匹配项。
当查询匹配到元音错误的单词时,您应该返回单词列表中的第一个这样的匹配项。
如果该查询在单词列表中没有匹配项,则应返回空字符串。
给出一些查询 queries,返回一个单词列表 answer,其中 answer[i] 是由查询 query = queries[i] 得到的正确单词。
思路:
符合条件的有三种可能:
1.当前单词在列表里;
2.列表转成小写字符,当前单词忽略大小写之后,在小写的列表里;
3.所有单词模糊元音字符,当前单词也忽略,在忽略元音的列表里;
但是,替换的单词是原列表的单词,
所以,用3个map分别记录原单词、全小写的单词、元音模糊单词的字符第一次出现的下标。三个map查找的优先级是先找原单词,再找小写单词,再找元音模糊单词。
对于元音字符,可以用正则替换成特殊字符,然后将新的字符作为key存储
如果三个map里面都找不到,就返回空字符串。
两个map也可以,不过数组的查找比map查找更消耗时间
时间复杂度O(n),空间复杂度O(n)
/**
* @param {string[]} wordlist
* @param {string[]} queries
* @return {string[]}
*/
const transform = (s) => {
return s.toLowerCase().replace(/[aeiou]/g, "*");
};
var spellchecker = function (wordlist, queries) {
const map2 = new Map();
const map3 = new Map();
const map = new Map();
const l = wordlist.length;
for (let i = 0; i < l; i++) {
map.set(wordlist[i], i);
if (!map2.has(wordlist[i].toLowerCase())) {
map2.set(wordlist[i].toLowerCase(), i);
}
const newWord = transform(wordlist[i]);
if (!map3.has(newWord)) {
map3.set(newWord, i);
}
}
return queries.map((item) => {
return (
wordlist[
map.get(item) ?? map2.get(item.toLowerCase()) ?? map3.get(transform(item))
] ?? ""
);
});
};
2.连续差相同的数字
题目:
返回所有长度为 n 且满足其每两个连续位上的数字之间的差的绝对值为 k 的 非负整数 。
请注意,除了 数字 0 本身之外,答案中的每个数字都 不能 有前导零。例如,01 有一个前导零,所以是无效的;但 0 是有效的。
你可以按 任何顺序 返回答案。
思路:递归处理。第一个数字不能是0,所以先循环从1-9取第一个数字,然后在递归函数里记录之前的取数结果以及剩余能取的数字的数量。
时间复杂度O(n),空间复杂度O(n)
/**
* @param {number} n
* @param {number} k
* @return {number[]}
*/
var numsSameConsecDiff = function(n, k) {
const res = [];
const dfs = (preN, s) => {
if (preN >= 10 || preN < 0) return;
if (s.length === n) {
res.push(Number(s));
return;
}
dfs(preN + k, `${s}${preN + k}`);
if (!k) return;
dfs(preN - k, `${s}${preN - k}`);
};
for (let i = 1; i <= 9; i++) {
dfs(i, `${i}`);
}
return res;
};
3.煎饼排序
题目:
给你一个整数数组 arr ,请使用 煎饼翻转 完成对数组的排序。
一次煎饼翻转的执行过程如下:
选择一个整数 k ,1 <= k <= arr.length
反转子数组 arr[0...k-1](下标从 0 开始)
例如,arr = [3,2,1,4] ,选择 k = 3 进行一次煎饼翻转,反转子数组 [3,2,1] ,得到 arr = [1,2,3,4] 。
思路:对于长度为l的数组,如何通过翻转进行排序呢?因为每次翻转的是1-k的数字,所以为了让之前排好序的数字不受后面的翻转的影响,可以将数字先放到最后
即将数字倒序排列,最后一次翻转得到结果,
对于长度为l的数组,先找到最小值的下标index,然后翻转前index个数字,再翻转前l个数字,即可将当前最小的数字翻转到最后
然后进行l-1长度时候的处理。
时间复杂度O(nlogn),空间复杂度O(n)。最不理想的时候,需要翻转2n+1次,
/**
* @param {number[]} arr
* @return {number[]}
*/
var pancakeSort = function(arr) {
const res = [];
const dfs = (len) => {
if (len < 2) return;
let min = Infinity;
let index;
for (let i = 0; i < len; i++) {
if (arr[i] < min) {
index = i;
min = arr[i];
}
}
if (index === len - 1) {
dfs(len - 1);
return;
}
if (index === 0) {
res.push(len);
for (let i = 0; i < len / 2; i++) {
[arr[i], arr[len - 1 - i]] = [arr[len - 1 - i], arr[i]];
}
dfs(len - 1);
return;
}
for (let i = 0; i <= index / 2; i++) {
[arr[i], arr[index - i]] = [arr[index - i], arr[i]];
}
for (let i = 0; i < len / 2; i++) {
[arr[i], arr[len - 1 - i]] = [arr[len - 1 - i], arr[i]];
}
res.push(index + 1);
res.push(len);
dfs(len - 1);
};
dfs(arr.length);
res.push(arr.length);
return res;
};
4.翻转二叉树以匹配先序遍历
题目:
给你一棵二叉树的根节点 root ,树中有 n 个节点,每个节点都有一个不同于其他节点且处于 1 到 n 之间的值。
另给你一个由 n 个值组成的行程序列 voyage ,表示 预期 的二叉树 先序遍历 结果。
通过交换节点的左右子树,可以 翻转 该二叉树中的任意节点。例,翻转节点 1 的效果如下:
请翻转 最少 的树中节点,使二叉树的 先序遍历 与预期的遍历行程 voyage 相匹配 。
如果可以,则返回 翻转的 所有节点的值的列表。你可以按任何顺序返回答案。如果不能,则返回列表 [-1]。
思路:用一个变量记录当前遍历的节点数,然后对二叉树进行先序遍历,对于任意一个节点,判断左右节点的情况,如果左右节点非空,且不等于当前的voyage[i],说明无法通过交换得到结果,所以返回-1.如果可以交换,就先交换然后继续遍历
时间复杂度O(n),空间复杂度O(h)
var flipMatchVoyage = function(root, voyage) {
const res = [];
let flag = true;
if (!root) return voyage.length ? [-1] : [];
if (root.val !== voyage[0]) return [-1];
let i = 1;
const dfs = (root) => {
if (!root) return;
if (!root.left && !root.right) return;
if (!root.left) {
if (root.right.val === voyage[i]) {
i++;
dfs(root.right);
} else {
flag = false;
}
} else {
if (root.left.val === voyage[i]) {
i++;
dfs(root.left);
if (!root.right) return;
if (root.right.val !== voyage[i]) {
flag = false;
} else {
i++;
dfs(root.right);
}
} else {
if (!root.right) {
flag = false;
} else if (root.right.val !== voyage[i]) {
flag = false;
} else {
[root.left, root.right] = [root.right, root.left];
res.push(root.val);
i++;
dfs(root.left);
if (root.right.val !== voyage[i]) {
flag = false;
} else {
i++;
dfs(root.right);
}
}
}
}
};
dfs(root);
return flag ? res : [-1];
};
5.最接近原点的k个点
题目:
我们有一个由平面上的点组成的列表 points
。需要从中找出 K
个距离原点 (0, 0)
最近的点。
(这里,平面上两点之间的距离是欧几里德距离。)
你可以按任何顺序返回答案。除了点坐标的顺序之外,答案确保是唯一的。
思路:每个点映射出对应的下标以及对应到原点的距离,通过原点距离进行排序,然后再截取前k个距离,返回对应下标的点
时间复杂度O(nlogn),空间复杂度O(logn)
/**
* @param {number[][]} points
* @param {number} k
* @return {number[][]}
*/
var kClosest = function(points, k) {
const dis = points.map((item, index) => {
return [Math.pow(item[0], 2) + Math.pow(item[1], 2), index];
});
dis.sort((a, b) => a[0] - b[0]);
return dis.slice(0, k).map((item) => points[item[1]]);
};