有趣的js编程题
- 一、旋转数组
- 二、请找出1-10000中的对称数字
- 三、给定一个数组将该数组的0项放到最后
- 四、实现一个方法可以将传入的数字逆序字符串形式输出
- 五、myInterval(fn,a , b, n)定时函数
- 六、rgb转成16进制的方法
- 七、promise.retry方法
- 八、数组扁平化、去重、排序
- 八、数组拼接
- 九、地址路径搜索匹配
- 十、两个大数相加
- 十一、斐波那契
- 十二、写一个函数验证一个数是否是素数
- 十三、求最大公约数
- 十四、判断是否是回文
- 十五、将数字12345678转化成RMB形式:12,345,678
- 十六、删除相邻相同的字符串
- 十七、连字符转成驼峰
- 十八、贪心算法
- 十九、击鼓传花
- 二十、最长公共前缀
- 二十一、爬楼梯问题
- 二十二、二叉树的最大深度
- 二十三、 组成最长的回文串
- 二十四、十进制整数M转化为N进制数
- 二十五、获取对象的最深层级
- 二十六、打印九九乘法表
- 二十七、版本号比较
- 二十八、商品分类
- 二十九、语句反转
- 三十、fecth请求并考虑请求超时
- 三十一、扁平数组转树形
- 三十二、并发限制的promise异步调度器
- 三十三、字符全排列
前端必备工具推荐网站(免费图床、API和ChatAI等实用工具):
http://luckycola.com.cn/
一、旋转数组
1、题目描述:
2、实现思路
- pop与unshift方法
- slice方法(不改变原数组,返回新数组)
- splice方法(改变原数组)
二、请找出1-10000中的对称数字
1、题目描述:
2、实现思路
构造数据列表的几种方式
- 筛选思路 revere倒转对比
2.数学规律构造思路
// 2、数学规律构造
// 1、2、3、...9
// 11、22...99
// 101、111、121...191、202...292、909...999
// 1001、1111、1221....2002、2112.....9009、9119...9999
function getLvLNums2() {
let res = [];
let i = 1;
for(;i <= 9; i++) {
res.push(i);
res.push(11 * i);
for(let j = 0; j <=9; j++) {
res.push(101 * i + 10 * j);
res.push(1001 * i + 110 * j);
}
}
return res.sort((a, b) => a - b)
}
console.log(getLvLNums2())
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
三、给定一个数组将该数组的0项放到最后
1、题目描述:
2、实现思路
1.filter筛选思路
2.push和splice思路
注意点: 需要一个变量来动态减少循环的次数
3.排序算法是思路–双指针思想
function afterZeroFn3(arr) {
let point1 = 0;
let i = 0;
for(;i < arr.length; i++) {
if (arr[i] === 0 && arr[point1] !== 0) {
point1 = i;
} else {
if (point1 < i && arr[i] != 0 && arr[point1] === 0) {
let temp = arr[i];
arr[point1] = temp;
arr[i] = 0;
point1 = i;
}
}
}
return arr;
}
console.log(afterZeroFn3(testArr));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
四、实现一个方法可以将传入的数字逆序字符串形式输出
1、题目描述:
2、实现思路
1.数组reserve思路
2.字符串递归截取思路
递归需要有一个合适的结束时机
- 数学取值思想(递归思想)
递归需要有一个合适的结束时机
五、myInterval(fn,a , b, n)定时函数
1、题目描述:
2、实现思路
1.闭包思路
function myInterval(fn, a, b, n) {
let _a = a;
let _b = b;
let _n = n;
let count = 0;
let _fn = fn;
function inFn() {
let time = _a + count * _b;
console.log('time=>', time)
if (count >= _n) {
return;
}
setTimeout(_ => {
_fn();
count++;
inFn();
}, time)
}
inFn();
}
myInterval(function(){
console.log('myInterval....');
}, 1000, 2000, 3);// 1s 3s 5s 7s
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
2.面向对象思想
unction myInterval(fn, a, b, n) {
this.a = a;
this.b = b;
this.fn = fn;
this.count = 1;
this.n = n;
}
// 正确实现
myInterval.prototype.start = function() {
let delay = this.a + this.count * this.b;
let timer = setTimeout(() => {
console.log('执行函数:', this.count);
this.fn();
this.count++;
this.start();
}, delay);
if (this.count > this.n) {
clearTimeout(timer);
console.log('清理定时器');
return;
};
};
let instance = new myInterval(function(){
console.log('执行了。。');
}, 1000, 2000, 3);
instance.start();
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
六、rgb转成16进制的方法
1、题目描述:
2、实现思路
1.思路分析
let testRGB = 'rgb(255, 255, 2)';
function TranfromColor() {
this.originColor = '';
this.colorArr = [];
this.result = '#';
// rgb(255, 255, 255)
// this.colorReg = /\d+/g;
this.colorReg = /^(rgb|RGB)\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/;
this.get16Color = function(rgbColor) {
this.originColor = rgbColor;
if (!this.originColor) return;
this.getColorArr(this.originColor).forEach((item, idx) => {
// this.result += ('0' + Number(item).toString(16)).substr(-2);
this.result += (Number(item) > 16 ? '' : '0') + Number(item).toString(16);
// this.result += ('' + Number(item).toString(16)).padStart(2, 0);
});
return this.result;
};
this.getColorArr = (originColor) => {
let matchRes = originColor.match(this.colorReg);
if (!matchRes) return;
// console.log(matchRes, matchRes[0], matchRes[1], matchRes[2]);
matchRes[2] && this.colorArr.push(matchRes[2]);
matchRes[3] && this.colorArr.push(matchRes[3]);
matchRes[4] && this.colorArr.push(matchRes[4]);
[...Array(3).keys()].forEach((item, indx) => {
+this.colorArr[item] > 255 && (this.colorArr[item] = '255')
});
return this.colorArr;
}
}
let tranfromColor = new TranfromColor();
let color16 = tranfromColor.get16Color(testRGB);
console.log(color16)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
七、promise.retry方法
1、题目描述:
2、实现思路
1.思路分析
Promise.retry = function(fn, times) {
return new Promise(async (resolve, reject) => {
while(true) {
try {
let res = await fn();
resolve(res);
console.log(res);
break;
} catch (error) {
if (times < 1) {
console.log('最终失败');
reject('fainaly fail');
break;
}
times--;
console.log('失败一次,剩余' + times + '次')
}
}
})
}
Promise.retry(testFn, 3).then((res) => {
console.log('okkk==>', res)
}, err => {
console.log('err===>', err);
})
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
八、数组扁平化、去重、排序
1、题目描述:
2、实现思路
1.思路分析
Array.prototype.flat = function() {
let arr = this;
let newArr = arr.map(item => {
if (Array.isArray(item)) {
return item.flat();
};
return [item];
});
console.log('中间状态:', newArr);
return [].concat(...newArr);
}
Array.prototype.unique = function() {
return [...new Set(this)];
}
// console.log(arrTest.flat());
Array.prototype.flat2 = function() {
let arr = this;
while(arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
};
console.log('中间状态2=》', arr)
return arr;
};
console.log(arrTest.flat2().unique().sort((a, b) => a -b));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
八、数组拼接
1、题目描述:
2、实现思路
1.思路分析
var arr1 = ['A1', 'A2', 'A3', 'A4', 'B1', 'B2', 'B3', 'B4', 'C1', 'C2', 'C3', 'D1'];
var arr2 = ['A', 'B', 'C', 'D'];
function getNewArr1(arr1, arr2) {
// 创建一个新数组,以免在原始数组上进行操作导致意外的副作用
let newArr = [...arr1];
let lastIndex = 0; // 跟踪arr1中当前分组的最后一个元素的索引
for (let i = 0; i < arr2.length; i++) {
// 找到当前分组(A, B, C, D)在arr1中的最后一个元素的索引
while (lastIndex < newArr.length && newArr[lastIndex].charAt(0) === arr2[i]) {
lastIndex++;
}
// 在分组的末尾插入arr2的当前元素
newArr.splice(lastIndex, 0, arr2[i]);
// 更新lastIndex,以便下次循环时能够找到下一个分组的末尾
lastIndex++;
}
return newArr;
}
// 关注过程的思路
function getNewArr2(arr1, arr2) {
for(let i = 0; i < arr2.length; i++) {
for (let index = 0; index < arr1.length; index++) {
if (index - 1 >= 0 && arr1[index - 1].charAt(0) == arr2[i] && arr1[index].charAt(0) != arr2[i]) {
arr1.splice(index, 0, arr2[i]);
index++;
}
}
}
arr1.push(arr2[arr2.length - 1])
return arr1;
}
// console.log(getNewArr2(arr1, arr2));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
九、地址路径搜索匹配
1、题目描述:
// 树状结构的关键词搜索
let testObj = {
babel: '北京市',
child: [
{
babel: '朝阳区',
child: [
{
babel: '西半街道',
},
{
babel: '向上向善',
}
]
},
{
babel: '昌平区',
child: [
{
babel: '香水百合',
},
{
babel: '昌平街道',
}
]
}
]
}
// 实现一个函数search可以进行关键词搜索,返回出关键词出现的链路
// 比如 search('西半') 返回 ['北京市', '朝阳区', '西半街道']
// 比如 search('朝阳区') 返回 ['北京市', '朝阳区']
// 比如 search('街道') 返回 ['北京市', '昌平区', '昌平街道']、 ['北京市', '朝阳区', '西半街道']
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
2、实现思路
1.思路分析
2.代码实现
function search(string, obj) {
let result = [];
function inSearch(obj, parentPath) {
let curPath = parentPath.concat(obj.babel);
if (obj.babel.search(string) !== -1) {
result.push(curPath);
}
if(obj.child && obj.child.length > 0) {
obj.child.forEach(child => {
inSearch(child, curPath);
});
}
}
inSearch(obj, []);
console.log(result);
return result;
}
search('街道', testObj)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
十、两个大数相加
1、题目描述:
2、实现思路
1.思路分析
let a = "9007199254740991";
let b = "1234567899999999999";
function add(a ,b){
//取两个数字的最大长度
let maxLength = Math.max(a.length, b.length);
//用0去补齐长度
a = a.padStart(maxLength , 0);//"0009007199254740991"
b = b.padStart(maxLength , 0);//"1234567899999999999"
//定义加法过程中需要用到的变量
let t = 0;
let f = 0; //"进位"
let sum = "";
for(let i=maxLength-1 ; i>=0 ; i--){
t = parseInt(a[i]) + parseInt(b[i]) + f;
f = Math.floor(t/10);
sum = t%10 + sum;
}
if(f == 1){
sum = "1" + sum;
}
return sum;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
十一、斐波那契
function fibonacci(n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
};
console.log(fibonacci(10));
// ---------------------类似的题目
// 求n! => n! = n * (n-1) * (n-2)...*2 * 1
function getResFn(n) {
if (n == 1 || n ==0) {
return 1;
}
return getResFn(n-1) * n;
}
// 写出求1+2+…+n、求1+1/2+1/3+..+1/n
function getResFn2(n) {
if (n <= 1) {
return 1;
}
return getResFn(n-1) + n;
}
function getResFn3(n) {
if (n <= 1) {
return 1;
}
return getResFn(n-1) + 1/n;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
十二、写一个函数验证一个数是否是素数
function isPrime(num){
if(num === 2 || num === 3) return true;
if (num <=1) return false;
if (num % 2 === 0) return false;
// 平方根
let sqrtN = Math.sqrt(num);
let dernum = 3;
while(sqrtN >= dernum) {
if (num % dernum === 0) {
return false;
} else {
dernum += 2;
}
}
return true;
}
console.log(isPrime(11)); // false
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
十三、求最大公约数
十四、判断是否是回文
十五、将数字12345678转化成RMB形式:12,345,678
function changeStr(numStr) {
let strArr = numStr.split('').reverse();
let resArr = [];
for (let index = 0; index < strArr.length; index++) {
resArr.push(strArr[index]);
if ((index + 1) % 3 === 0) {
resArr.push(',')
}
};
return resArr.reverse().join('');
}
console.log('changeStr:', changeStr('12345565'))
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
十六、删除相邻相同的字符串
十七、连字符转成驼峰
十八、贪心算法
1、题目描述:
function greedyCoinChange(coins, amount) {
// 排序 从大到小
coins.sort((a, b) => b - a);
// 需要的硬币数量
let coinNum = 0;
// 剩余金额数
let remainingAmount = amount;
// 不同额度对应的硬币数量
let amountCountObj = {};
for (let i = 0; i < coins.length; i++) {
let curCoinNum = Math.floor(remainingAmount / coins[i]);
coinNum += curCoinNum;
// 更新剩余金额
remainingAmount = remainingAmount % coins[i];
amountCountObj[coins[i]] = curCoinNum;
if (remainingAmount == 0) {
break;
};
};
if (remainingAmount != 0) {
throw Error('找零失败');
}
return {coinNum, amountCountObj};
}
console.log("greedyCoinChange:", greedyCoinChange([10, 5, 2, 7], 47))
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
十九、击鼓传花
1、题目描述:
class Gun {
constructor(people) {
this.people = people;
this.curIndex = 0;
this.lastPeople = '';
this.isrunning = false;
};
startGame() {
if (this.isrunning) {
throw Error('游戏正在进行中');
}
this.isrunning = true;
let inRunningFn = () => {
let time = Math.random() * 3;
setTimeout(() => {
this.curIndex = (this.curIndex + 1) % this.people.length;
inRunningFn();
}, time);
};
inRunningFn();
};
stopGame() {
if (!this.isrunning) {
throw Error('游戏未开始,无需停止');
};
this.isrunning = false;
this.lastPeople = this.people[this.curIndex];
console.log('拿到花的人是:', this.lastPeople);
return this.lastPeople ;
}
}
let GunInstance = new Gun(['小红', '小蓝', '小花', '小小']);
GunInstance.startGame();
setTimeout(() => {
GunInstance.stopGame();
}, 5000)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
二十、最长公共前缀
1、题目描述:
function longestCommonPrefix(strArr) {
// 最大前缀的长度
let k = strArr[0].length;
for(let i = 0; i < strArr.length; i++) {
// 比较和更新k
k = k > strArr[i].length ? strArr[i].length : k;
// 查找k的小范围
for(let j = 0; j < k; j++) {
if (strArr[0][j] != strArr[i][j]) {
k = j;
}
}
}
return strArr[0].substring(0, k);
}
console.log('longestCommonPrefix:', longestCommonPrefix(['qwe123', 'qwe188883', 'qwe1999']))
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
二十一、爬楼梯问题
1、题目描述:
function climbStairs(n) {
// 保存爬楼梯的方式数量
let dp = new Array(n + 1);
// <2小于两级台阶的情况
dp[1] = 1;
dp[2] = 2;
if (n <= 2) return n;
// 3级别台阶以上的情况
for (let index = 3; index <= n; index++) {
dp[index] = dp[index-1] + dp[index-2];
};
return dp[n];
}
console.log('climbStairs:', climbStairs(1), climbStairs(2), climbStairs(4))
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
二十二、二叉树的最大深度
1、题目描述:
function maxDepth(root) {
// 如果根节点不存在(即二叉树为空),函数直接返回深度为 0。
if (!root) {
return 0;
} else {
// 如果根节点存在
let left = maxDepth(root.left);// 递归地调用 maxDepth 函数来计算右子树的最大深度
let right = maxDepth(root.right);
// Math.max() 函数找出左右子树的最大深度,并加上当前节点的深度 1,即为以当前节点为根的子树的最大深度。然后将这个深度返回给调用者。
return Math.max(left, right) + 1;
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
二十三、 组成最长的回文串
1、题目描述:
function longestPalindrome(str) {
// 最终长度
let length = 0;
let mySet = new Set();
for (let index = 0; index < str.length; index++) {
const curstr = str[index];
if (mySet.has(curstr)) {
length += 2;
mySet.delete(curstr);
} else {
mySet.add(curstr);
}
}
if (mySet.size > 0) {
length++;
}
return length;
}
console.log('longestPalindrome:', longestPalindrome('abccccdd'))
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
二十四、十进制整数M转化为N进制数
1、题目描述:
function decimalToNBase(M, N) {
// 进制在2~36
if (N < 2 || N > 36) throw Error('N只能在2-36之间的整数');
if (M == 0) return '0';
// 结果
let numToResult = '';
// 除N取余 直到M为零
while(M > 0) {
let remNum = M % N;
// 判断余数是否大于10
if (remNum > 10) {
// 转化成A-F 通过unicode编码转字符
let willaddNum = String.fromCharCode((remNum - 10) + 'A'.charCodeAt());
numToResult += remNum;
} else {
// 直接拼接结果
numToResult += remNum;
}
// 更新除数
M = Math.floor(M / N);
};
return numToResult;
}
console.log('decimalToNBase:', decimalToNBase(0, 8))
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
二十五、获取对象的最深层级
1、题目描述:
function getDeepNum2(obj, deep = 0) {
if (typeof obj != 'object' || obj == null) {
return deep;
};
let maxLength = deep;
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
maxLength = Math.max(maxLength, getDeepNum2(obj[key], deep + 1))
}
}
return maxLength;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
二十六、打印九九乘法表
二十七、版本号比较
1、题目描述
* 请实现函数 compareVersions ,对两个版本号进行比较,并满足以下条件:
* @param {String} verA (必传)需要比较的新版本号
* @param {String} verB (必传)用于比较的旧版本号
* @param {String} type (可选)比较方案
* gt:大于
* gte:大于等于
* lt:小于
* lte:小于等于
* eq:等于
* @return {String|Boolean} 不传入type时,返回比较值,大于返回1、等于返回0、小于返回-1
传入type时,返回判断值 bool
// 运行结果验证
[
compareVersions('2.0', '1.0.0'), // 1
compareVersions('1', '1.0.0'), // 0
compareVersions('1.2.3', '2.1.0'), // -1
compareVersions('1.2.0', '1.10.0'), // -1
compareVersions('1.2.3', '2.1.0', 'gt'), // false
compareVersions('1.2.3', '2.1.0', 'eq'), // false
compareVersions('1.2.3', '2.1.0', 'lt'), // true
compareVersions('1.0.0', '1', 'gte'), // true
compareVersions('1.0.0', '1', 'gt'), // false
compareVersions('1.0.0', '1', 'eq'), // true
];
// [1, 0, -1, -1, false, false, true, true, false, true]
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
2、代码实现
function compareVersions(verA, verB, type) {
// 分割版本号
verA = verA.split('.');
verB = verB.split('.');
// 比较版本号
function compare(a, b) {
const lenA = a.length;
const lenB = b.length;
const maxLength = Math.max(lenA, lenB);
for (let i = 0; i < maxLength; i++) {
const numA = i < lenA ? +a[i] : 0;
const numB = i < lenB ? +b[i] : 0;
if (numA > numB) {
return 1;
} else if (numA < numB) {
return -1;
}
}
return 0;
}
// 根据 type 参数返回结果
if (type) {
switch (type) {
case 'gt':
return compare(verA, verB) === 1;
case 'gte':
return compare(verA, verB) >= 0;
case 'lt':
return compare(verA, verB) === -1;
case 'lte':
return compare(verA, verB) <= 0;
case 'eq':
return compare(verA, verB) === 0;
default:
throw new Error('type无效');
}
} else {
return compare(verA, verB);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
二十八、商品分类
1、题目描述
* 请写一个数据结构转换函数 convert
* 现有商品列表数据 itemList
* 将该商品列表按照商品分类(category)进行分组,且分组间按该分类下商品的种数进行倒序排序(休闲零食有四种商品,因此排第一);
* 在每一分组下按商品的销量(saleCount)进行倒序排序。
* 要求不能对原数据 itemList 的任意属性值进行修改
// 运行结果验证
convert(itemList);
/* [
* {
* "category": "休闲零食",
* "items": [
* { "id": 7, "name": "商品7", "category": "休闲零食", "saleCount": 48 },
* { "id": 4, "name": "商品4", "category": "休闲零食", "saleCount": 42 },
* { "id": 6, "name": "商品6", "category": "休闲零食", "saleCount": 37 },
* { "id": 9, "name": "商品9", "category": "休闲零食", "saleCount": 26 }
* ]
* },
* {
* "category": "家居百货",
* "items": [
* ...
* ]
* },
* ...
* ]
*/
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
2、代码实现
function convert(itemList) {
// 使用Map数据结构按category分组
const groupedItems = new Map();
for (const item of itemList) {
const category = item.category;
if (!groupedItems.has(category)) {
groupedItems.set(category, []);
}
groupedItems.get(category).push(item);
}
// 转换Map为数组,并根据分组中商品数量进行排序
const sortedGroups = [...groupedItems].sort((a, b) => b[1].length - a[1].length);
// 对每个分组内的商品按销量进行排序
sortedGroups.forEach(group => {
group[1].sort((a, b) => b.saleCount - a.saleCount);
});
// 转换回所需格式的对象数组
return sortedGroups.map(([category, items]) => ({
category,
items
}));
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
二十九、语句反转
1、题目描述
2、代码实现
三十、fecth请求并考虑请求超时
1、题目描述
2、代码实现
// Promise.race方式
function myFetch(url. options, time = 1000 * 10) {
let timeOutPromise = new Promise((resolve. reject) => {
setTimeout(() => {
reject('请求超时');
}, time);
});
let fetchPromise = fetch(url, options);
return Promise.race([timeOutPromise, fetchPromise]).catch(err => {
console.log('myFetch catch err:', err);
throw err;
})
};
// AbortController方式
function myFetch2(url, options, time = 10 * 1000) {
let constr = new AbortController();
const { signal } = constr;
setTimeout(() => {
constr.abort();
}, time);
return fetch(url, {...options, signal}).then(res => {
return res.json();
}).catch(err => {
console.log('myFetch2 err');
throw err;
});
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
三十一、扁平数组转树形
1、题目描述
如何将一个扁平数组的数据结构,转换为树形结构。
例如,有数组 arr 如下:
const arr = [
{ id: 1, pid: 0 },
{ id: 2, pid: 1 },
{ id: 3, pid: 1 },
{ id: 4, pid: 2 },
{ id: 5, pid: 2 },
{ id: 6, pid: 3 },
];
要求编写一个函数 arr2tree(arr),得到输出结果如下
{
"id": 0,
"children": [
{
"id": 1,
"children": [
{
"id": 2,
"children": [
{
"id": 4
},
{
"id": 5
}
]
},
{
"id": 3,
"children": [
{
"id": 6
}
]
}
]
}
]
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
2、代码实现
function arr2tree(arr) {
// 创建一个映射,用于存储每个节点的引用
const map = {};
// 初始化根节点
map[0] = { id: 0, children: [] };
// 遍历数组,为每个节点创建引用,并初始化 children 数组
arr.forEach(item => {
map[item.id] = { ...item, children: [] };
});
// 遍历数组,构建树形结构
arr.forEach(item => {
// 查找父节点
const parent = map[item.pid];
// 如果找到了父节点,则将当前节点添加到父节点的 children 数组中
if (parent) {
parent.children.push(map[item.id]);
}
});
// 返回根节点(pid 为 0 的节点)
return map[0];
}
const arr = [
{ id: 1, pid: 0 },
{ id: 2, pid: 1 },
{ id: 3, pid: 1 },
{ id: 4, pid: 2 },
{ id: 5, pid: 2 },
{ id: 6, pid: 3 },
];
const tree = arr2tree(arr);
console.log(JSON.stringify(tree, null, 2)); // 格式化输出树形结构
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
三十二、并发限制的promise异步调度器
1、题目描述
32 const scheduler = new Scheduler();
33 const timeout = (time, order) => {
34 return new Promise(resolve => {
35 setTimeout(() => {
36 resolve(order);
37 }, time);
38 });
39 };
40 const addTask = (time, order) => {
41 scheduler.add(() => timeout(time, order));
42 };
43 addTask(4000, 1);
44 addTask(1000, 2);
45 addTask(900, 3);
46 addTask(3000, 4);
// 当没有并发限制的时使其出处结果为: 2 3 4 1
请实现一个并发数是2的promise异步调度器scheduler,使其出处结果为: 2 3 1 4
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
2、代码实现
class Scheduler {
constructor() {
this.max = 2;// 最大限制数
this.work = [];// 正在执行的任务盒子
this.unwork = [];// 等待的任务盒子
}
add(promise) {
if (this.work.length < this.max) {
//还没达到最大数量限制,可以直接执行
this.runTask(promise);
} else {
// 此时任务盒子数量达到并发最大数量,那就放在等待区域
this.unwork.push(promise);
}
}
runTask(promise) {
this.work.push(promise);
promise()
.then(res => {
console.log(res);
})
.finally(() => { // 任务执行完毕,就立马从执行任务盒子删掉
let index = this.work.indexOf(promise);
this.work.splice(index, 1); // 如果任务等待盒子还有任务,那就继续执行
if (this.unwork.length) {
this.runTask(this.unwork.shift());
}
});
}
}
const scheduler = new Scheduler();
const timeout = (time, order) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(order);
}, time);
});
};
const addTask = (time, order) => {
scheduler.add(() => timeout(time, order));
};
addTask(4000, 1);
addTask(1000, 2);
addTask(900, 3);
addTask(3000, 4);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
三十三、字符全排列
1、题目描述
2、代码实现
function permute(s, l = s.length, p = [], result = []) {
// 如果当前排列的长度等于原始字符串的长度,那么它是一个有效的排列,将其添加到结果数组中
if (p.length === l) {
result.push(p.join(''));
return;
}
for (let i = 0; i < l; i++) {
// 如果当前字符已经使用过,则跳过
if (p.includes(s[i])) continue;
// 将当前字符添加到排列中
p.push(s[i]);
// 递归调用permute来继续构建下一个字符的排列
permute(s, l, p, result);
// 回溯,即撤销上一步的选择,以便尝试其他字符
p.pop();
}
return result;
}
// 示例使用
const str = "ABC";
console.log(permute(str));
// 输出: ["ABC", "ACB", "BAC", "BCA", "CBA", "CAB"]
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.