寻找两个数组的中位数
js
function find(arr, arr2) {
let arr = [...arr, ...arr2].sort((a, b) => a - b);
// 先对数组合并后排序
let middleNum = Math.floor(arr.length / 2);
// 取中位数
if (arr.length % 2 === 0) return (arr[middleNum - 1] + arr[middleNum]) / 2;
// 如果能被2整除,则返回最中间两个数的平均数
return arr[middleNum];
// 不能被整除,返回最中间那个数
}
两数相加
js
// 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
// 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
// 您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
var addTwoNumbers = function (l1, l2) {
let c = 0;
let r = new ListNode();
let p = r;
let p1 = l1;
let p2 = l2;
while (p1 || p2 || c) {
c += (p1 && p1.val) + (p2 && p2.val);
p.next = new ListNode(c % 10);
c = parseInt(c / 10);
p1 && (p1 = p1.next);
p2 && (p2 = p2.next);
p = p.next;
}
return r.next;
};
罗马数组转整数
js
// 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
// 字符 数值
// I 1
// V 5
// X 10
// L 50
// C 100
// D 500
// M 1000
// 例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
// 通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
// I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
// X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
// C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
// 给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
/**
* @param {string} s
* @return {number}
*/
var romanToInt = function (s) {
const map = {
I: 1,
V: 5,
X: 10,
L: 50,
C: 100,
D: 500,
M: 1000,
};
let req = 0;
for (let i = 0; i < s.length; i++) {
if (i < s.length && map[s[i]] < map[s[i + 1]]) {
req -= map[s[i]];
} else {
req += map[s[i]];
}
}
return req;
};
合并两个有序链表
js
// 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
var mergeTwoLists = function (l1, l2) {
if (l1 === null) return l2;
let cur = l1;
while (l2) {
let node = new ListNode();
if (cur.val >= l2.val) {
node.val = cur.val;
node.next = cur.next;
cur.val = l2.val;
cur.next = node;
l2 = l2.next;
} else if (cur.next && l2.val <= cur.next.val) {
node.val = l2.val;
node.next = cur.next;
cur.next = node;
l2 = l2.next;
} else if (!cur.next) {
node.val = l2.val;
node.next = null;
cur.next = node;
l2 = l2.next;
continue;
}
cur = cur.next;
}
return l1;
};
整数倒序输出
用 JavaScript 写一个函数,输入 int 型,返回整数逆序后的字符串。如:输入整型 1234,返回字符串“4321”。
要求必须使用递归函数调用,不能用全局变量,输入函数必须只有一个参数传入,必须返回字符串。
js
// 小于10只有一个数组,直接返回
// 大于等于10,把最后数字放最前面,并拼接,剩余部分递归调用的返回值
function getReverse(num) {
return num < 10
? num.toString()
: `${num % 10}${getReverse(Math.floor(num / 10))}`;
}
各位相加
js
// 给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。
/**
* @param {number} num
* @return {number}
*/
var addDigits = function (num) {
let arr = [];
for (let index of num.toString()) {
arr.push(index);
}
if (arr.length === 1) {
return parseInt(arr[0]);
}
let number = 0;
for (let i = 0; i < arr.length; i++) {
number += parseInt(arr[i]);
}
if (number > 9) {
return addDigits(number);
} else {
return number;
}
};
种花问题
js
// 605. 种花问题
// 假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
// 给定一个花坛(表示为一个数组包含0和1,其中0表示没种植花,1表示种植了花),和一个数 n 。能否在不打破种植规则的情况下种入 n 朵花?能则返回True,不能则返回False。
/**
* @param {number[]} flowerbed
* @param {number} n
* @return {boolean}
*/
var canPlaceFlowers = function (flowerbed, n) {
let max = 0;
for (let i = 0; i < flowerbed.length - 1; i++) {
if (flowerbed[i] === 0) {
if (i === 0 && flowerbed[1] === 0) {
max++;
i++;
} else if (flowerbed[i - 1] === 0 && flowerbed[i + 1] === 0) {
max++;
i++;
}
}
}
return max >= n;
};
卡牌分组
给定一副牌,每张牌上都写着一个整数。此时,你需要选定一个数字 X,使我们可以将整副牌按下述规则分成 1 组或更多组:
- 每组都有 X 张牌。
- 组内所有的牌上都写着相同的整数。
- 仅当你可选的 X >= 2 时返回 true。
js
/**
* @param {number[]} deck
* @return {boolean}
*/
var hasGroupsSizeX = function (deck) {
// 最大公约数计算公式
function gcd(num1, num2) {
// 利用辗转相除法来计算最大公约数
return num2 === 0 ? num1 : gcd(num2, num1 % num2);
}
// 相同牌出现次数Map
let timeMap = new Map();
// 遍历牌
deck.forEach(num => {
// 统计每张牌出现的次数
timeMap.set(num, timeMap.has(num) ? timeMap.get(num) + 1 : 1);
});
// Map.protype.values()返回的是一个新的Iterator对象,所以可以使用扩展运算符(...)来构造成数组
let timeAry = [...timeMap.values()];
/*
最大公约数
因为该数组是出现次数数组,最小值至少为1(至少出现1次),所以默认赋值为数组首位对公约数计算无干扰
*/
let g = timeAry[0];
// 遍历出现次数,计算最大公约数
timeAry.forEach(time => {
// 因为需要比较所有牌出现次数的最大公约数,故需要一个中间值
g = gcd(g, time);
});
// 判断是否满足题意
return g >= 2;
};
删除字符串中的所有相邻重复项
js
// 给你一个字符串 s,「k 倍重复项删除操作」将会从 s 中选择 k 个相邻且相等的字母,并删除它们,使被删去的字符串的左侧和右侧连在一起。
// 你需要对 s 重复进行无限次这样的删除操作,直到无法继续为止。
// 在执行完所有删除操作后,返回最终得到的字符串。
/**
* @param {string} s
* @param {number} k
* @return {string}
*/
var removeDuplicates = function (s, k) {
let stack = [];
for (const c of s) {
let prev = stack.pop();
if (!prev || prev[0] !== c) {
stack.push(prev);
stack.push(c);
} else if (prev.length < k - 1) {
stack.push(prev + c);
}
}
return stack.join("");
};
将整数转换为两个无零整数的和
js
// 「无零整数」是十进制表示中 不含任何 0 的正整数。
// 给你一个整数 n,请你返回一个 由两个整数组成的列表 [A, B],满足:
// A 和 B 都是无零整数 并且 A + B = n
/**
* @param {number} n
* @return {number[]}
*/
var getNoZeroIntegers = function (n) {
let time = 1;
while (String(time).includes("0") || String(n - time).includes("0")) {
time++;
}
return [time, n - time];
};
控制执行顺序和延误时间的类
js
// 先延迟2s,打印1,延迟1s,打印2,在延迟1s,打印3
new O().print(1).wait(1).print(2).wait(1).print(3).firstWait(2);
class O {
constructor() {
this.callbackList = []; // 任务中心
// setTimeout延迟自动执行,保证任务都已加入任务中心
setTimeout(() => {
next();
});
}
next() {
// 取出任务中心最前面一个任务,如果存在就执行
let nextCallback = this.callbackList.shift();
if (nextCallback) {
nextCallback();
}
}
print(value) {
const that = this; // 保存this的引用
// 用自执行函数把任务函数包起来,保持对参数value的引用
const fun = (function (param) {
return function () {
console.log(param);
that.next(); //当任务执行时,在打印之后立即执行下一个任务
};
})(value);
this.callbackList.push(fun);
//把当前打印的任务,推送到任务中心
return this; //返回当前实例,可以链式调用实例的方法
}
wait(time) {
const that = this;
const fun = (function (param) {
return function () {
console.log("输出等待时间:", param);
that.next();
setTimeout(() => {
that.next(); // 等待time时间后,再执行下一个任务
}, param * 1000);
};
})(time);
this.callbackList.push(fun);
return this;
}
firstWait(time) {
const that = this;
const fun = (function (param) {
return function () {
console.log("输出首先等待时间:", param);
that.next();
setTimeout(() => {
that.next();
}, param * 1000);
};
})(time);
this.callbackList.unshift(fun);
// 推送到任务中心首位,会先等待再执行下一个任务
return this;
}
}
整数数组分组
js
let array = [2, 10, 3, 4, 5, 11, 10, 11, 20];
// 转成: [[2, 3, 4, 5], [10, 11], [20]]
function formatArray(array) {
let result = [];
array
.sort((a, b) => a - b)
.forEach(item => {
let remainder = Math.floor(item / 10);
(result[remainder] || (result[remainder] = [])).push(item);
});
return result.filter(item => item);
}
formatArray(array);
字符串大小写转化
js
// AbC to aBc
function transformChar(string) {
let char = "";
for (let index = 0; index < string.length; index++) {
let code = string.charCodeAt(index);
let thransCode = code + (code > 90 ? -32 : 32);
// 小写比大写字母code 码大32:a-z=32
char += String.fromCharCode(thransCode);
}
return char;
}
console.log(transformChar("AbC"));
数组每个值移动 n 个位置
js
function reverseArray(array, n) {
let length = array.length;
let result = [];
for (let index = 0; index < length; index++) {
const element = array[index];
let aferIndex = (index + n) % length;
// 索引相加后取模,既是移动之后的位置
result[aferIndex] = element;
}
return result;
}
console.log(reverseArray([1, 2, 3, 4, 5, 6, 7], 8));
找出 1000 内对称的数
把数字反转后依然相等,就是对称的数。
js
[...Array(1000).keys()].filter(item => {
return (
item > 10 && item === Number(item.toString().split("").reverse().join(""))
);
});
数组中 0 移动到最后面
要求:只在 array 上更改
js
let array = [0, 1, 0, 3, 0, 12, 0];
let length = array.length;
let moveNum = 0; //记录移动的个数
for (let index = 0; index < length - moveNum; ) {
//仅需遍历index前length - moveNum的数字,后面都是移动后的0了
if (array[index] === 0) {
array.push(array.splice(index, 1)[0]);
// 把=0的数,截取到数组最后面
// 数字被截取,需要继续遍历当前index的值
moveNum++;
} else {
index++;
}
}
console.log(array);
实现 add 函数
满足以下功能。
js
add(1); // 1
add(1)(2); // 3
add(1)(2)(3); // 6
add(1)(2, 3); // 6
add(1, 2)(3); // 6
add(1, 2, 3); // 6
console.log(add(1, 2, 3));
function add(...a) {
let sum = a.reduce((p, n) => p + n);
function next(...b) {
let _sum = b.reduce((p, n) => p + n);
sum = sum + _sum;
return next;
}
next.toString = function () {
return sum;
};
return next;
}
树结构找出父级 id 数组
1121 => [1,11,112,1121]
js
function findParents(array, params) {
let length = params.length;
let findArray = array;
let result = [];
for (let index = 0; index < length; index++) {
const element = params.slice(0, index + 1);
// 每次截取前index-1的字符:这是当前父级的id
const parent = findArray.find(item => item.id === element);
// 根据父级id找出当前父级
if (!parent) return [];
// 找不到父级,宣告失败,直接返回空数组
result.push(element);
// 找到了父级,把父级id添加到结果数组里
if (index === length - 1) {
return result;
// 当前已经是最后一位了,返回结果
} else {
if (parent?.children?.length) {
findArray = parent.children;
// 当前父级若存在子项,则从子项里寻找下一个父级id
} else {
return [];
// 当前父级没有子项,无法寻找下一个父级id,则宣告失败
}
}
}
}
每次走 1 或者 2 步阶梯,n 阶梯有多少种走法?
js
// 方法一
function getNumber(n) {
if (n <= 2) return n;
return getNumber(n - 1) + getNumber(n - 2);
}
// 方法二 时间复杂度低
function getNumber(n, map = new Map()) {
if (n <= 2) return n;
if (map.get(n) !== null) return map.get(n);
let result = getNumber(n - 1, map) + getNumber(n - 2, map);
map.set(n, result);
return result;
}
斐波那契函数,输入 n,求其数列的第 n 项
js
function getNumber(n) {
if (n <= 2) return 1;
return getNumber(n - 1) + getNumber(n - 2);
}
// methods two 时间复杂度低
function getNumber(n, map = new Map()) {
if (n <= 2) return 1;
if (map.get(n)) return map.get(n);
let result = getNumber(n - 1, map) + getNumber(n - 2, map);
map.set(n, result);
return result;
}
leetcode 1
整数数组中,求两个数之和为 target 的两个数的索引 o(n~2)
js
// 双指针思想
function getIndex(array, target) {
for (let i = 0; i < array.length; i++) {
for (let j = i + 1; j < array.length; j++) {
if (array[i] + array[j] == target) return [i, j];
}
}
}
// 使用map缓存遍历过的数值o(n)
function getIndex(array, target) {
let map = new Map();
for (let i = 0; i < array.length; i++) {
let another = target - array[i];
if (map.has(another)) return [map.get(another), i];
map.set(array[i], i);
}
}
数组合并相关
两个整数升序数组 num1,num2,元素个数分别为 m,n,把 num2 数组有序合并到 num1 中,
js
// 方法一 填充到另一个数组后,排序
function combine(num1, num2, m, n) {
for (let i = 0; i < n; i++) {
num1[m + i] = num2[i];
}
return num1.sort();
}
// 方法二 双指针思想 o(m+n)
function combine(num1, num2, m, n) {
let total = m + n;
let tem = [];
for (let index = 0, num1Index = 0, num2Index = 0; index < total; index++) {
if (num1Index >= m) {
// num1 先取完了
tem[index] = num2[num2Index++];
} else if (num2Index >= n) {
// num2 先取完了
tem[index] = num1[num1Index++];
} else if (num1[num1Index] <= num2[num2Index]) {
tem[index] = num1[num1Index++];
// 把比较小的数先放到要输出的数组里,并把较小值的index++
} else {
tem[index] = num2[num2Index++];
}
num1 = tem;
}
return num1;
}
// 方法三 也是双指针思想,倒着计算,少一个tem变量 o(m+n)
// num1或者num2的长度,肯定都小于他们两的长度之和m+n。
// 把比较的结果放在>=他们最大index的地方,不影响各自原本的元素
function combine(num1, num2, m, n) {
let total = m + n;
for (
let index = total - 1, num1Index = m - 1, num2Index = n - 1;
index < 0;
index--
) {
if (num1Index < 0) {
num1[index] = num2[num2Index--];
} else if (num2Index < 0) {
break;
} else if (num1[num1Index] > num2[num2Index]) {
num1[index] = num1[num1Index--];
} else {
num1[index] = num2[num2Index--];
}
}
return num1;
}
leecode 448
在 n 个整数数组 array 中,求不在区间[1,n]中的数值组成的数组
js
// 方法一
function getNumberArray(array, n) {
let result = [...Array(n + 1).keys()].shift();
// 拿到[1,n]这个区间
return result.filter(item => !array.includes(item));
// 从区间里筛选中不在array数组里的元素
}
getNumberArray([1, 2, 3, 4, 7, 8, 3, 2]);
// 方法二 翻牌思想:把匹配的打个标记,后续用作区分
function getNumberArray(array, n) {
for (let iterator of array) {
if (iterator < 1 || iterator > n) array[index] += n;
// 不在[1,n]之间的,都翻牌加n:最后在区间的元素都<=n
}
return array.filter(item => item <= n);
// 筛选数组中所有<=n的元素
}
getNumberArray([1, 2, 3, 4, 7, 8, 3, 2]);
leetcode 20
有效符号(){}[] 返回 true [(})] 返回 false
js
function isValidChar(string) {
let result = [];
for (let char of string) {
if (char === "(" || char === "[" || char === "{") {
result.push(char); // 左括号入栈
} else {
let pre = result[result.length - 1];
if (
(pre === "(" && char === ")") ||
(pre === "[" && char === "]") ||
(pre === "{" && char === "}")
) {
result.pop();
// 否则,判断与栈顶元素能否匹配。匹配就把栈顶元素出栈
} else {
return false; // 不匹配,说明元字符传存在不对称,返回false
}
}
}
return result.length === 0;
// 栈内不存在元素,说明全部都匹配到了
}
console.log(isValidChar("()[]{}"));
console.log(isValidChar("[(})]"));
相邻字符问题
js
// leetcode 1047 去除字符串中相邻重复字符
// 方法一
function delRepeat(string) {
for (let i = 0; i < string.length; ) {
if (string[i] === string[i + 1]) {
string = string.slice(0, i) + string.slice(i + 2);
// 当前元素和下一个元素相等,就截掉这两个元素。继续从当前index判断
} else {
i++; //否则从下一个位置开始遍历
}
}
return string;
}
// 方法二
function delRepeat(string) {
let result = [];
for (const iterator of string) {
let pre = result.pop();
if (iterator !== pre) {
result.push(pre, iterator);
// 栈顶元素和当前元素不同,把当前元素也入栈。否则去除栈顶元素
}
}
return result.join("");
}
console.log(delRepeat("qqwerttr"));
// leetcode 3 找出字符中无重复的最长子串:滑动窗口思想
function maxLength(string) {
let length = string.length;
let leftIndex = 0; // 滑动窗口左指针
let maxLength = 0; //最长长度
let maxChar = ""; //最长的字符
// 字符改变时更改滑动窗口,并计算最多字符和长度
function getChar(index) {
let preCharLength = index - leftIndex;
// 获取当前滑动窗口长度
if (preCharLength > maxLength) {
maxLength = preCharLength;
maxChar = string[leftIndex];
}
leftIndex = index;
// 把滑动窗口重置为当前字符index
}
for (let index = 0; index < length; index++) {
let char = string[index];
if (index === length - 1) {
getChar(char === string[leftIndex] ? index + 1 : index);
// 最后一个元素,无论是否相等都需要计算。相同计算长度要包含自身
} else {
char !== string[leftIndex] && getChar(index);
// 不是最后一个元素时,只有当前字符不属于滑动窗口字符时需要计算
}
}
return [maxLength, maxChar];
}
leetcode 71 简化路径
类似 node path 模块的 path.parse(),拼接路径字符串
js
function getPath(path) {
let result = [];
path.split("/").forEach(item => {
if (item === "..") {
result.pop();
// 存在‘往上一级’,则去掉栈顶元素
} else if (item && item !== ".") {
result.push(item);
// 元素不等于‘.’,推送到栈顶
}
});
return result.length ? "/" + result.join("/") : "/";
}
console.log(getPath("/home/../ab/cd/")); // /ab/cd
验证是否是回文字符串
回文字符串:去除空格和无效字符后左右对称
js
// 双指针思想
function isPalindrome(s) {
s = s.toLowerCase();
function isValid(char) {
return (char >= "a" && char <= "z") || (char >= "0" && char <= "9");
}
let leftIndex = 0;
let rightIndex = s.length - 1;
let leftChar = "";
let rightChar = "";
while (leftIndex <= rightIndex) {
leftChar = s[leftIndex];
rightChar = s[rightIndex];
if (!isValid(leftChar)) {
leftIndex++;
} else if (!isValid(rightChar)) {
rightIndex--; // 左/右指针无效时略过
} else if (leftChar !== rightChar) {
return false; // 最左字符!==最右字符,不是回文字符
} else {
leftIndex++; // 左右相等,同时往里移动指针
rightIndex--;
}
}
return true;
}
信号灯问题
js
// 实现 红灯10s,黄灯3s,绿灯5s
let sig = new Signal({
init: "red",
colors: ["red", "yellow", "green"],
times: [10, 3, 5],
});
console.log(sig);
class Signal {
constructor(options) {
this.signal = options.init; //当前信号灯
this.colors = options.colors; //自定义颜色数组
this.times = options.times; // 时间数组,和颜色一一对应
this.eventMap = new Map();
// 事件中心:对外提供事件,用于监听信号灯
this.eventMap.set("change", new Set());
// 监听红绿灯切换事件
this.eventMap.set("tick", new Set());
// 每1s时间 tick 通知外界
this.setTime(); //初始化当前灯开始和结束时间
this.exchange(); // 定时1s查询剩余时间
}
get next() {
//获取下一个灯的颜色
return this.colors[
(this.colors.indexOf(this.signal) + 1) % this.colors.length
];
}
get remain() {
//获取当前灯亮的剩余时间
let diff = this.end - Date.now();
if (diff < 0) {
diff = 0;
}
return diff;
}
on(event, handler) {
this.eventMap.get(event).add(handler);
}
off(event, handler) {
this.eventMap.get(event).delete(handler);
}
emit(event) {
this.eventMap.get(event).forEach(handler => {
handler.call(this, this);
});
}
async exchange() {
await 1;
if (this.remain > 0) {
this.emit("tick");
await sleep(1000); //沉睡1s
} else {
this.signal = this.next; //切换到下一个灯
this.setTime(); //设置灯的开始和结束时间
this.emit("change"); //通知灯的change事件
console.log("切换了:", this.signal);
}
this.exchange();
}
setTime() {
//灯亮时,设置当前灯的开始和结束时间
this.start = Date.now();
this.end = this.start + this.times[this.colors.indexOf(this.signal)] * 1000;
}
}
根据数字按键得到所有字母组合
js
// 根据数字按键得到所有字母组合
function getKeybordMap(digits) {
let map = [, , "abc", "def", "ghi", "jkl", "mno", "pqps", "tuv", "wxyz"];
// 获取每个数字键(即数组index),对应的字母
let result = [];
for (let digit of digits) {
result = compose(result, map[digit].split(""));
// 把当前按键对应的字母,分别和结果数组里的元素混合,混合结果组成新数组
}
function compose(arr1, charts) {
if (arr1.length === 0) return charts;
if (charts.length === 0) return arr1;
const composeResult = [];
for (let item of arr1) {
for (let chart of charts) {
composeResult.push(item + chart);
}
}
return composeResult;
}
return result;
}
console.log(getKeybordMap("234"));
快速排序算法
快速排序算法:找出基准值,大于和小于基准值的数放入不同数组里,两个数组递归该操作
js
function quickSort(array) {
if (array.length <= 1) return array; // 终止条件
let baseValue = array[Math.floor(array.length / 2)];
//数组中间值作为基准值
let left = [];
let right = [];
for (let element of array) {
if (element < baseValue) {
left.push(element);
} else {
right.push(element);
}
}
return quickSort(left).concat(quickSort(right));
}
小孩报数去除报 3 问题
小孩子围成一个圈,从 1 开始报数,报数为 3 的小孩子不在计数,然后又从 1 开始报数。找出剩下的最后一个孩子的索引 index
js
/** 翻牌思想,报3的孩子索引设为-1,不在参数计数
* @param {Number} num 孩子总数
* @param {Number} count 要去除的报数
*/
function destroyThree(num, count) {
let pool = [...Array(num).keys()];
// 创建[0,num-1]的池子
let index = 0; //报数孩子的index
let counter = 0; //要报的数字
let exitCount = 0; //出局孩子的个数
while (num - exitCount > 1) {
// 剩余孩子个数超过1,需要继续报数
if (pool[index] !== -1) {
//当前孩子没有出局时,报数比上个孩子报数+1,
if (counter++ === count) {
pool[index] = -1;
exitCount++;
counter = 0;
// 如果正好=3,翻牌,另出局数+1,计数重置为0
}
}
index++ === num && (index = 0);
// 指针移到下一个孩子。如果超过最大索引,重置为0
}
return pool.findIndex(item => item !== -1);
// 返回最后一个没有被翻牌的孩子的index
}
最多的元素和次数
js
//查找文章中出现次数最多的单词和次数
function mostWord(article) {
if (!article) return;
article = article.trim().toLowerCase();
// 大小写都按同一个单词处理,并去除首尾空格
let wordList = [...new Set(article.match(/[a-z]+/g))];
// 文章以空格分隔成单词数组后,去重减少遍历次数
let maxWord = ""; // 最多次的单词
let maxNum = 0; // 单词出现最多次数
wordList.forEach(word => {
let reg = new RegExp(" " + word + " ", "g");
let wordnum = article.match(reg).length;
// 拿到匹配到的每个单词,组成的数组的长度(即出现次数)
if (wordnum > maxNum) {
maxNum = wordnum;
maxWord = word;
}
});
return `次数最多的单词是:${maxWord},次数是:${maxNum}`;
}
// leetcode 1207 求字符串中出现次数最多的字符和次数
function getTimes(string) {
let maxNum = 0;
let maxChar = "";
let map = new Map();
for (const iterator of string) {
map.set(iterator, (map.get(iterator) || 0) + 1);
}
for (const [key, value] of map) {
if (value > maxNum) {
maxNum = value;
maxChar = key;
}
}
return [maxChar, maxNum];
}
求数组全排列
js
//释放注释为求字符全排列
function getArray(params) {
const res = []; // 结果集数组
let path = []; // 字符组合数组
function backTracking(array, used) {
// 参数1:需要全排的数组 参数2:used数组记录当前元素是否已被使用
let arrayLength = array.length;
if (path.length === arrayLength) return res.push(path); // 当获取的元素个数等于传入数组长度时(此时说明找到了一组结果)
for (let i = 0; i < arrayLength; i++) {
if (used[i]) continue; // 当前元素已被使用,结束此次循环
path.push(array[i]); // 将符合条件的元素存进path数组
// path = path + array[i]; // 将符合条件的元素存进path字符串
used[i] = true; // 并将该元素标为true,表示已使用同支
backTracking(array, used);
path = path.slice(0, path.length - 1);
used[i] = false;
}
}
// backTracking(Array.from(params), []); // 当为
backTracking(params, []);
return res;
}
console.log(getArray([1, 2, 3, 4]));
找出最少硬币组合
js
const rninCoinChange = new MinCoinChange([1, 5, 10, 25]);
console.log(rninCoinChange.makeChange(36)); // [1, 10, 25]
function MinCoinChange(coins) {
var cache = {};
this.makeChange = function (amount) {
if (!amount) return [];
const _this = this;
if (cache[amount]) return cache[amount];
let min = [];
let newMin;
let newAmount;
for (let i = 0; i < coins.length; i++) {
const coin = coins[i];
newAmount = amount - coin;
if (newAmount >= 0) {
newMin = _this.makeChange(newAmount);
}
if (
newAmount >= 0 &&
(newMin.length < min.length - 1 || !min.length) &&
(newMin.length || !newAmount)
) {
// 这里设定了边界条件,当传给递归函数的 newAmount 为 coin 时开始回溯
min = [coin].concat(newMin);
console.log("new min " + min + " for " + amount);
}
}
return (cache[amount] = min);
};
}
实现两个大整数相加
实现超出整数存储范围的两个大整数相加 function add(a,b)。注意 a 和 b 以及函数的返回值都是字符串。
js
function add(a, b) {
let maxLength = Math.max(a.length, b.length);
let padA = a.padStart(maxLength, "0");
let padB = b.padStart(maxLength, "0");
// 为了计算时个位数对齐,填充长度至两者长度的最大值
let carryNum = 0; // 是否进1
let result = [];
for (let index = maxLength - 1; index >= 0; index--) {
// 从个数开始计算,也就是倒着算。
let tem = Number(padA[index]) + Number(padB[index]) + carryNum;
if (tem >= 10) {
carryNum = 1; // 超10进1
result.unshift(tem % 10);
index === 0 && result.unshift(carryNum);
// 遍历到最后一位时,如果超10,往最前位进1
} else {
carryNum = 0; // 不超10,进0,
result.unshift(tem);
}
}
return result.join("");
}
console.log(add("333333333333333333333333333333", "33333333333333333333333"));
求数组交集
js
function getjiaoji(arr1, arr2) {
return arr1.filter(item => {
let index = arr2.findIndex(v => v == item);
if (index !== -1) {
arr2.splice(index, 1);
// 把当前元素删除,防止重复使用
return true;
}
});
}
数组指定值
js
// 整数数组中某两个数等于某个值的所有可能性,用过的元素不在使用
//二分法+双指针思想实现
const arr = [13, 2, 534, 2, 12, 232, 23, 12, 12, 2, 131, 1, 31, 21];
function getSum(arr, num) {
arr.sort((a, b) => a - b);
const result = [];
let left = 0,
let right = arr.length - 1;
while (left < right) {
if (arr[left] + arr[right] < num) {
left++;
} else if (arr[left] + arr[right] > num) {
right--;
} else {
result.push([arr[left], arr[right]]);
left++;
right--;
}
}
return result;
}
// 双循环+双指针思想
function getSum(array, num) {
const length = array.length;
const result = [];
for (let index = 0; index < length - 1; index++) {
for (let innerIndex = index + 1; innerIndex < length; innerIndex++) {
// 遍历外层循环指针后面的元素
if (array[index] + array[innerIndex] === num) {
result.push([array[index], array.splice(innerIndex, 1)[0]]);
//后面用过的元素删掉,并保存结果
}
}
}
return result;
}
console.log(getSum(arr, 14));
// 二分法查找指定值在整数有序数组中的位置
function binarySearch(arr, target) {
let low = 0; // 范围的最小值索引
let high = arr.length - 1; // 范围的最大值索引
let midIndex = 0; // 范围中间值索引
let midElement = 0; // 范围中间值索引对应的中间值
while (low <= high) {
midIndex = Math.floor((low + high) / 2);
midElement = arr[midIndex];
if (target > midElement) {
low = midIndex + 1;
} else if (target < midElement) {
high = midIndex - 1;
} else {
return midIndex;
}
}
return -1; // 最终找不到等于target的值,则返回-1
}
console.log(binarySearch([3, 4, 7, 10, 34], 7));
// leetcode 1207 数组元素是否都独一无二出现
function isNoRepeat(array) {
let map = new Map();
for (const iterator of array) {
map.set(iterator, (map.get(iterator) || 0) + 1);
}
let set = new Set();
for (const [key, value] of map) {
set.add(key); // 利用了Set自动去重的特性
}
return set.size === map.size;
// return array.length === [...new Set(array)].size;
}
leetcode 933
统计最近 3000 毫秒内的请求次数,很少遇到
js
class RecentCounter {
result = [];
ping(t) {
this.result.push(t);
while (t - this.result[0] >= 3000) {
this.result.shift();
}
return this.result.length;
}
}
let obj = new RecentCounter();
console.log(obj.ping(3000));
console.log(obj.ping(4000));
console.log(obj.ping(5000));
数组中是否有重复元素
js
function hasRepeat(array) {
let set = new Set();
for (const iterator of array) {
if (set.has(iterator)) return true;
// 后面的元素存在缓存过的,就有重复
set.add(iterator);
//元素没有缓存就加入Set缓存
}
return false;
}
寻找质数
找出某范围内的所有质数
js
function getPrime(params) {
if (params <= 1)
throw "注意 1 不是质数,质数是大于 1 的且只能被 1 和自身整除的自然数";
let result = []; // 质数结果集合
let innerIndex = 0;
// 内循环索引,定义在外层方便外层循环可以拿到内循环的index
for (let index = 2; index < params; index++) {
for (innerIndex = 2; innerIndex < index; innerIndex++) {
if (index % innerIndex === 0) break;
// 2 <= innerIndex < index范围内存在被整除的数,是非质数
}
innerIndex === index && result.push(index);
// 内层循环走到了最后都没找到被整除的数,是质数
}
return result;
}
console.log(getPrime(10)); // [ 2, 3, 5, 7 ]