Skip to content

JavaScript 编程题目

1.二维数组取值排列组合成新二维数组

题目

有一个数组:

javascript
const arr = [[1,2],3,[4,5,6]];

定义一个函数,传入arr后,返回值为一个二维数组:

javascript
[[1,3,4],[2,3,4],[1,3,5],[2,3,5],[1,3,6],[2,3,6]]

解法

javascript
function f(arr) {
    // 用于存放最后结果的空数组
    var ret = []
    // 函数result
    function fi(result, i) {
        if (i === -1) {
            ret.push(result)
        } else {
            let items = arr[i]
            if (!Array.isArray(items)) {
                items = [items]
            }
            items.forEach(item => {
                fi([item,...result], i - 1)
            });
        }
    }
    fi([], arr.length - 1)
    return ret
}
const arr = [[1,2],3,[4,5,6]];
console.log(f(arr))

补充:(用babel转为ES5)

javascript
function _toConsumableArray(arr) { 
    if (Array.isArray(arr)) { 
        for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { 
            arr2[i] = arr[i]; 
        } 
        return arr2; 
    } else { 
        return Array.from(arr); 
    } 
}

function f(arr) {
    var ret = [];
    function fi(result, i) {
        if (i === -1) {
            ret.push(result);
        } else {
            var items = arr[i];
            if (!Array.isArray(items)) {
                items = [items];
            }
            items.forEach(function (item) {
                fi([item].concat(_toConsumableArray(result)), i - 1);
            });
        }
    }
    fi([], arr.length - 1);
    return ret;
}
var arr = [[1, 2], 3, [4, 5, 6]];
console.log(f(arr));

2.数组中添加新字段组成新数组

题目

如何提取数组中的字段,然后添加新的字段,获取数据如下:

javascript
var data = [
{"id":"1","name":"华为","data":"25u6s8f545d3"},
{"id":"2","name":"小米","data":"cd58de9d3c5d"},
];

想获得的数据格式如下:

javascript
var data = [
{"id":"1","name":"华为","data":"25u6s8f545d3","mac":"25:u6:s8:f5:45:d3"},
{"id":"2","name":"小米","data":"cd58de9d3c5d","mac":"cd:58:de:9d:3c:5d"},
];

解法

javascript
var data = [
  { "id": "1", "name": "华为", "data": "25u6s8f545d3" },
  { "id": "2", "name": "小米", "data": "cd58de9d3c5d" },
];
data.forEach(item => {
  item.mac = item.data.replace(/\w{2}\B/g, '$&:')
})
console.log(data)

3.实现类似数字的倒金字塔式打印

题目

打印类似效果:

javascript
4444
333
22
1
22
333
4444

解法

javascript
function f(n) {
    for (let i = -n; i <= n; i++) {
        if (i === 0 || i === 1) {
            continue
        }
        let k = Math.abs(i)
        console.log(k.toString().repeat(k))
    }
}
f(4);

4.实现让字符串换行,每7个字符换行一次

题目

让字符串"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 每7个字符换行一次

javascript
aaaaaaa
aaaaaaa
aaaaaaa
aaaaaaa
aa

解法

javascript
var str='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
var result = str.replace(/(.{7})/g, '$1\n')
console.log(result);

5.自定义类似数组的数组类

题目

请写一段js程序,定义一个列表类List,该类包括两个成员:属性length(表示列表中的元素个数)和方法add(向列表添加元素),其中要求构造函数和add方法的参数为动态参数。

解法

javascript
class List{
    constructor(...items){
        this.items = items
        this.length = items.length
    }
    add(...items) {
        this.items.push(...items)
        this.length = this.items.length
    }
}

6.JavaScript遍历Json对象里的数组

题目

比如有这样一个对象数组:

javascript
let data = [
    {
        title: '标题一',
        tagName: 'h1'
    },
    {
        title: '标题二',
        tagName: 'h1'
    },
    {
        title: '标题三',
        tagName: 'h2'
    },
    {
        title: '标题四',
        tagName: 'h3'
    },
    {
        title: '标题五',
        tagName: 'h2'
    },
    {
        title: '标题六',
        tagName: 'h1'
    },
    {
        title: '标题七',
        tagName: 'h1'
    },
    {
        title: '标题八',
        tagName: 'h2'
    },
    {
        title: '标题九',
        tagName: 'h3'
    },
    {
        title: '标题十',
        tagName: 'h3'
    }
]

要求根据tagName的优先级,从h1开始排,一直到h6,每个h1后面直到下个h1之前的都是它的children节点,依此类推,比如上面的数据这样处理后就会像下面的格式:

javascript
let data = [
    {
      title: '标题一',
      tagName: 'h1'
    },
    {
      title: '标题二',
      tagName: 'h1',
      children: [
        {
          title: '标题三',
          tagName: 'h2'
          children: [
             {
                 title: '标题四',
                 tagName: 'h3'
             }
          ]
        },
        {
          title: '标题五',
          tagName: 'h2'
        }
      ]
    },
    {
      title: '标题六',
      tagName: 'h1'
    },
    {
      title: '标题七',
      tagName: 'h1',
      children: [
        {
          title: '标题八',
          tagName: 'h2',
          children: [
            {
              title: '标题九',
              tagName: 'h3'
            },
            {
              title: '标题十',
              tagName: 'h3'
            }
          ]
        }
      ]
    }
]

解法

javascript
function collect(arr, i, parent) {
    if (i >= arr.length) {
        return i
    }
    let current = arr[i]
    if (current.tagName > parent.tagName) {
        parent.children.push(current)
    } else {
        return i
    }
    i++
    let next = arr[i]
    if (!next) {
        return i
    }
    if (next.tagName > current.tagName) {
        current.children = []
        i = collect(arr, i, current)
    }
    return collect(arr, i, parent)
}

var ret = {
    tagName: 'h0',
    children: []
}
collect(data, 0, ret)

console.log(ret.children)

7.红包的随机分配

题目

使用random函数每次随机分配。每次得出的红包值大于 [0.01],小于 [剩余金额-剩余人数*0.01],最后一个人获得剩余全部。

解法

javascript
function rp(total, n) {
  var remain = total
  var ret = []
  for (let i = 0; i < n - 1;i++) {
    let m = Math.ceil(Math.random() * 100 * (remain - (n - (i + 1)) * 0.01)) / 100
    ret.push(m)
    remain -= m
  }
  ret.push(Number(remain.toFixed(2)))
  return ret
}

8.简化函数

题目

简化下面的函数:

javascript
function mergeJsonObject(jsonObj1, jsonObj2, jsonObj3, jsonObj4, jsonObj5) {
    let resultJsonObject = {};
    function jsonObj(jsonObj) {
        for (let attr in jsonObj) {
                resultJsonObject[attr] = jsonObj[attr];
            }
        }
        jsonObj(jsonObj1);
        jsonObj(jsonObj2);
        jsonObj(jsonObj3);
        jsonObj(jsonObj4);
        jsonObj(jsonObj5);
        return resultJsonObject;
    }

解法

javascript
function mergeJsonObject(...args) {
    let resultJsonObject = {};
    function jsonObj(jsonObj) {
        for (let attr in jsonObj) {
            resultJsonObject[attr] = jsonObj[attr];
        }
    }
    args.forEach(jsonObj)
    return resultJsonObject;
}

9.JS中将特定格式的字符串转化为json格式的问题

题目

一段字符串如下:

javascript
表名1@字段1~表名1@字段2~表名2@字段1~表名2@字段2

如何将其合理地处理为json格式,如:

javascript
{
    表名1:[字段1,字段2],
    表名2:[字段1, 字段2]
}

解法

javascript
var str = '表名1@字段1~表名1@字段2~表名2@字段1~表名2@字段2'

var obj = str.split('~').reduce((state, item) => {
  var [tname, fname] = item.split('@')
  if (state[tname]) {
    state[tname].push(fname)
  } else {
    state[tname] = [fname]
  }
  return state
}, {})

10.根据对象的属性合并对象

题目

某个对象:

javascript
var prd = {
    "id": 1,
    "department_id": 42,
    "products": [{
            "id": 12,
            "name": "49da",
            "grouped_addons": [{
                "addons": [{
                        "id": "0_0_40",
                        "name": "rice",
                        "qty": 0,
                        "unit_price": "5.00"
                    },
                    {
                        "id": "0_0_41",
                        "name": "what",
                        "qty": 1,
                        "unit_price": "15.00"
                    }
                ]
            }]
        },
        {
            "id": 12,
            "name": "49da",
            "grouped_addons": [{
                "addons": [{
                    "id": "0_0_40",
                    "name": "rice",
                    "qty": 0,
                    "unit_price": "5.00"
                }, {
                    "id": "0_0_41",
                    "name": "what",
                    "qty": 1,
                    "unit_price": "15.00"
                }]
            }]
        },
        {
            "id": 42,
            "name": "345dd",
            "grouped_addons": [{
                "addons": [{
                    "id": "0_0_42",
                    "name": "rice",
                    "qty": 0,
                    "unit_price": "5.00"
                }, {
                    "id": "0_0_43",
                    "name": "what",
                    "qty": 1,
                    "unit_price": "15.00"
                }]
            }]
        },
        {
            "id": 48,
            "name": "33ffg",
            "grouped_addons": [{
                "addons": [{
                    "id": "0_0_44",
                    "name": "rice",
                    "qty": 0,
                    "unit_price": "5.00"
                }, {
                    "id": "0_0_45",
                    "name": "what",
                    "qty": 1,
                    "unit_price": "15.00"
                }]
            }]
        },
        {
            "id": 48,
            "name": "33ffg",
            "grouped_addons": [{
                "addons": [{
                    "id": "0_0_44",
                    "name": "rice",
                    "qty": 1,
                    "unit_price": "5.00"
                }, {
                    "id": "0_0_45",
                    "name": "what",
                    "qty": 3,
                    "unit_price": "15.00"
                }]
            }]
        }
    ]
}

想要将prd中的products中id相同的对象中的grouped_addons内id相同的qty相加合并,最终的结果想要如下:

javascript
var prd = {
    "id": 1,
    "department_id": 42,
    "products": [{
            "id": 12,
            "name": "49da",
            "grouped_addons": [{
                "addons": [{
                        "id": "0_0_40",
                        "name": "rice",
                        "qty": 0,
                        "unit_price": "5.00"
                    },
                    {
                        "id": "0_0_41",
                        "name": "what",
                        "qty": 2,
                        "unit_price": "15.00"
                    }
                ]
            }]
        },
        {
            "id": 42,
            "name": "345dd",
            "grouped_addons": [{
                "addons": [{
                    "id": "0_0_42",
                    "name": "rice",
                    "qty": 0,
                    "unit_price": "5.00"
                }, {
                    "id": "0_0_43",
                    "name": "what",
                    "qty": 1,
                    "unit_price": "15.00"
                }]
            }]
        },
        {
            "id": 48,
            "name": "33ffg",
            "grouped_addons": [{
                "addons": [{
                    "id": "0_0_44",
                    "name": "rice",
                    "qty": 1,
                    "unit_price": "5.00"
                }, {
                    "id": "0_0_45",
                    "name": "what",
                    "qty": 4,
                    "unit_price": "15.00"
                }]
            }]
        }
    ]
}

解法

javascript
var mp = prd.products.reduce((obj, item) => {
    if (!obj[item.id]) {
        obj[item.id] = [item]
    } else {
        obj[item.id].push(item)
    }
    return obj
}, {})
prd.products = Object.keys(mp).map(id => {
    return mp[id].reduce((state, item) => {
        item.grouped_addons[0].addons.forEach(addon => {
            var item = state.grouped_addons[0].addons.find(a => a.id === addon.id)
            item.qty += addon.qty
        })
        return state
    })
})
console.log(prd)

11.字符串重复打印

题目

写一个叫做 laugh() 的函数,它有一个参数n,表示要返回的 "ha" 的数量。

解法

javascript
// ES6
function laugh (n) {
    // ES6中`repeat`方法返回一个新字符串,表示将原字符串重复`n`次。参数如果是小数,会被取整。
    return 'ha'.repeat(n)
}

// ES5
function laugh (n) {
    var result = ''
    for (var i = 0; i < n; i++) {
        result += 'ha'
    }
    return result
}

12.按规律输出字符串

题目

从数字 1 循环访问到 20
如果数字可以被 3 整除,则输出 “Julia”
如果可以被 5 整除,则输出 “James”
如果可以同时被 3 和 5 整除,则输出 “JuliaJames”
如果不能被 3 或 5 整除,则输出该数字

解法

javascript
for (var i = 1; i < 21; i++) {
(function (n) {
    test(n);
})(i)
}
function test(num) {
  if ((num % 3 === 0) && (num % 5 === 0)) {
      console.log("JuliaJames");
  }
  if  (num % 3 === 0) {
      console.log("Julia");
  }
  if(num % 5 === 0) {
      console.log("James");
  }
  console.log(num);

13.函数参数分配

题目

假设现在有两个函数function A()和function B(),现在希望创建一个新的函数function C(),新函数的逻辑是将自己接收到的前两个参数传给函数A,剩余所有参数传给函数B,请用原生javascript实现函数C
举例:
如果调用函数C:C(a,b,c,d,e)
相当于调用函数A和函数B:A(a,b)和B(c,d,e)

解法

javascript
// ES5
function C() {
    var args = [].slice.call(arguments)
    A.apply(null, args.slice(0, 2))
    B.apply(null, args.slice(2))
}

// ES6 展开运算符
function a(v1, v2){
  console.log(v1, v2);
}

function b(...v){
  console.log(v);
}

function c(...all){
  a(all[0], all[1]);
  b(...(all.slice(2)));
}

c(1, 2, 3, 4, 5, 6, 7);

14.二次封装函数

题目

已知函数 fn 执行需要 3 个参数。请实现函数 partial,调用之后满足如下条件:
1、返回一个函数 result,该函数接受一个参数
2、执行 result(str3) ,返回的结果与 fn(str1, str2, str3) 一致

解法

javascript
function partial(fn,str1,str2) {
    var result = function(str3) {
        return fn.call(null,str1,str2,str3);
    }
    return result;
}

15.单词模式

题目

给定一种 pattern(模式) 和一个字符串 str ,判断 str 是否遵循相同的模式。
这里的遵循指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应模式。

示例1:

输入: pattern = "abba", str = "dog cat cat dog"
输出: true

示例2:

输入:pattern = "abba", str = "dog cat cat fish"
输出: false
说明:
你可以假设 pattern 只包含小写字母, str 包含了由单个空格分隔的小写字母。

解法

思路:

用JS ES6的Map结构,将pattern和str作为键值对传入数组,如果Map中存在pattern中的键,则比较Str中值;如果不存在键,则看Str中的值在Map中是否存在。如果键和值都在Map中,则将键值对加入Map中
javascript
var wordPattern = function(pattern, str) {
    let map = new Map();
    //将str转为数组
    words = str.split(" ");
    //遵循指完全匹配,长度不等,直接返回
    if(pattern.length !== words.length) return false;
    for (let i = 0; i < words.length; i++){
        //判断map中是否存在该键,如果存在且值不等,则返回false
        if (map.has(pattern[i])){
            if (map.get(pattern[i]) !== words[i]) {
                    return false;
                }
        } else{
            //如果不存在键,但值存在,也返回false
            //由于map没有查看值的方法,只能去所有的值然后遍历,不知道这里有没有简便的处理方式
            let hasValue = [...map.values()].some(value => value === words[i]);
            if (hasValue) {
                return false;
            }
            //键值都不存在,则添加键值对至map中
            map.set(pattern[i], words[i]);
        }
    }
    return true;    
};

16.有效的字母异位词

题目

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的一个字母异位词。

示例1:

输入: s = "anagram", t = "nagaram"
输出: true

示例2:

输入: s = "rat", t = "car"
输出: false

解法

javascript
var isAnagram = function(s, t) {
    if(s.length != t.length){
        return false;
    }
    let arrs = s.split("").sort();
    let arrt = t.split("").sort();
    for(let i = 0 ; i < arrs.length; i++){
        if(arrs[i] != arrt[i]){
            return false;
        }
    }
    return true;
};

17.数组去重

题目

对数组进行去重操作,只考虑数组中元素为数字或字符串,返回一个去重后的数组。

解法

对数组进行去重操作,只考虑数组中元素为数字或字符串,返回一个去重后的数组。

javascript
// ES3
function unique(arr) {
    var newArr = [];
    for (var i = 0; i < arr.length; i++) {
        var currElem = arr[i];

        if (newArr.indexOf(currElem) < 0) {
            newArr.push(currElem);
        }
    }

    return newArr;
}   
console.log(unique([1, 2, 1, 3]));

// ES5
function unique(arr) {
    return arr.filter(function (elem, index) {
        return arr.indexOf(elem) === index
    });
}
console.log(unique([1, 2, 1, 3]));

// ES6
var arr = [1, 2, 1, 3];
console.log(Array.from(new Set(arr)));

// ES7
var arr = [1, 2, 1, 3];
console.log([...new Set(arr)]);

18. 数组找单

题目

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

解法

需要维护一个对象来记录每一个元素出现的次数,使用元素的值作为key,元素出现的次数作为value。之后再遍历这个对象,找到value为1的key。对应的key就是那个元素。

javascript
function singleNumber(nums) {
  const obj = {};
  for (let i = 0; i < nums.length; i++) {
    obj[nums[i]] = obj[nums[i]] ? obj[nums[i]] + 1 : 1;
  }
  for (let key in obj) {
    if (obj[key] === 1) {
      return Number(key); // 由于 key 是 string ,因此我们这里需要转化下
    }
  }
}

console.log(singleNumber([2, 2, 1, 4, 4, 5, 5, 1, 8])); // 8

上面那种解法,创建了一个新的对象来储存结果,如果想不使用额外空间,明显是不行的。那么有没有办法可以只使用原来的数组来实现这个功能呢? 解决方案:异或操作

1.异或运算满足交换律、结合律。
1^2^...^n^...^n^...^1000,无论这两个n出现在什么位置,都可以转换成为1^2^...^1000^(n^n)的形式。

2.其次,对于任何数x,都有x^x=0,x^0=x。
所以1^2^...^n^...^n^...^1000 = 1^2^...^1000^(n^n)= 1^2^...^1000^0 = 1^2^...^1000(即序列中除了n的所有数的异或)。
异或运算是对于二进制数字而言的,比如说一个有两个二进制a、b,如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
而javascript的按位异或(即^操作)操作,则会对两个数字相应的每一对比特位执行异或操作。
比如说 1 ^ 2,本质上其实是1和2的每一对比特位执行异或操作,等价于下面
  00000000000000000000000000000001 // 数字1对应的二进制
^ 00000000000000000000000000000010 // 数字2对应的二进制
= 00000000000000000000000000000011 // 数字3对应的二进制
复制代码因此1^2的结果就为3啦。
那么如果两个相同的数字进行异或操作,结果就可想而知,答案为0啦。
如果是0和任何一个数字异或呢?结果是数字本身。

我们只需要遍历数组,将所有的值取异或,最终剩下的值,就是那个只出现一次的数字。 假设我们有一个数组,里面元素为[a, a, c, c, b, b, d]。那么我们对数组里的所有元素进行按位异或操作,即a ^ a ^ c ^ c ^ b ^ b ^ d,是不是就等价于0 ^ 0 ^ 0 ^ d = d。而d就是数组里只出现一次的元素。 那么我们可以扩展一下,对于任意满足某个元素只出现一次以外,其余每个元素均出现两次的数组,是不是可以通过这种方式来得到那个只出现一次的元素。

我们知道,两个相同的数字进行按位异或操作,得到的结果为0。并且任何数字与0进行按位异或,得到的结果是数字本身。那么假设我们有数组[a, b, b, a, c],将数组所有元素进行按位异或操作,即a ^ b ^ b ^ a ^ c,结果是不是等价于0 ^ c = c。同理可得,我们一个数组里只有某个元素只出现一次,其他都出现两次,那么将数组的所有元素都进行按位异或操作,那么结果是不是就等于那个只出现一次的元素。

javascript
/**
 * 只存在一次的数字
 * https://leetcode-cn.com/explore/interview/card/top-interview-questions-easy/1/array/25/
 * @ param {number[]} nums
 * @ return {number}
 */
function singleNumber(nums) {
  for (let i = 1; i < nums.length; i++) {
    nums[0] ^= nums[i];
  }
  return nums[0];
};

console.log(singleNumber([2, 2, 1, 4, 4, 5, 5, 1, 8]));

19.数组扁平化处理

题目

javascript
// 实现一个flatten方法,使得输入一个数组,该数组里面的元素也可以是数组,该方法会输出一个扁平化的数组。
// Example
let givenArr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10];
let outputArr = [1,2,2,3,4,5,5,6,7,8,9,11,12,12,13,14,10]

// 实现flatten方法使得
flatten(givenArr)——>outputArr

解法

javascript
function flatten(arr){
    var res = [];
    for(var i=0;i<arr.length;i++){
        if(Array.isArray(arr[i])){
            // concat() 方法用于连接两个或多个数组。返回一个新数组。
            res = res.concat(flatten(arr[i]));
        }else{
            res.push(arr[i]);
        }
    }
    return res;
}

进阶解法1(迭代器)

javascript
function flatten(arr){
    // prev 上一次调用回调时返回的累积值。如果没有提供初始值,则将使用数组中的第一个元素。item 数组中正在处理的元素。
    // array.reduce(function(accumulator, currentValue, currentIndex, arr), initialValue)
    // accumulator 和currentValue的取值有两种情况:
    // 如果调用reduce()时提供了initialValue,accumulator取值为initialValue,currentValue取数组中的第一个值;
    // 如果没有提供 initialValue,那么accumulator取数组中的第一个值,currentValue取数组中的第二个值。
    return arr.reduce(function(prev,item){
        return prev.concat(Array.isArray(item) ? flatten(item) : item);
    },[]);
}

进阶解法2(ES6拓展运算符)

javascript
function flatten(arr){
    while(arr.some(item => Array.isArray(item))){
        arr = [].concat(...arr);
    }
    return arr;
}

20.异步函数并行执行,并控制并行数量

题目

异步函数并行执行,并控制并行数量

实际场景

现在有个场景:

请你实现一个并发请求函数concurrencyRequest(urls, maxNum),要求如下:

要求最大并发数 maxNum 每当有一个请求返回,就留下一个空位,可以增加新的请求 所有请求完成后,结果按照 urls 里面的顺序依次打出(发送请求的函数可以直接使用fetch即可)

javascript
 const preloadManger = (urls, maxCount = 5) => {
  let count = 0; // 计数 -- 用于控制并发数
  const createTask = () => {
    if (count < maxCount) {
      const url = urls.pop(); // 从请求数组中取值
      if (url) {
        // 无论请求是否成功,都要执行taskFinish
        loader(url).finally(taskFinish);
        // 添加下一个请求
        count++;
        createTask();
      }
    }
  };

  const taskFinish = () => {
    count--;
    createTask();
  };

  createTask();
};

// 进行异步请求
const loader = async (url) => {
  const res = await fetch(url).then(res=>res.json());
  console.log("res",res);
  return res
}

const urls = [];
for (let i = 1; i <= 20; i++) {
    urls.push(`https://jsonplaceholder.typicode.com/todos/${i}`);
}

preloadManger(urls, 5)

解法

javascript
// 简洁版pLimit
// 异步逻辑并行执行,并控制并行数量
// 一个队列来保存任务,有两个时机考虑触发任务执行:
// 1. 开始的时候一次性执行最大并发数的任务 
// 2. 然后每执行完一个启动一个新的
const shortPLimit = (concurrency) => {
  // 传入并发数量
  if (!((Number.isInteger(concurrency) || concurrency === Infinity) && concurrency > 0)) {
    throw new TypeError('Expected `concurrency` to be a number from 1 and up');
  }
  
  // 添加的并发任务要进行排队,所以我们准备一个 queue
  const queue = [];
  // 记录当前在进行中的异步任务
  let activeCount = 0;

  // 下一步处理自然就是把活跃任务数量减一,然后再跑一个任务
  const next = () => {
    activeCount--;

    if (queue.length > 0) {
      // 如果队列中还有任务,就再跑一个
      queue.shift()();
    }
  };

  // 计数,运行这个函数,改变最后返回的那个 promise 的状态
  const run = async (fn, resolve, ...args) => {
    activeCount++;
    // 运行传入的异步函数
    const result = (async () => fn(...args))();

    resolve(result);

    try {
      // 等待异步函数执行完
      await result;
    } catch {}

    // 执行完之后进行下一步处理
    next();
  };

  // 把一个异步任务添加到 queue 中,并且只要没达到并发上限就再执行一批任务
  const enqueue = (fn, resolve, ...args) => {
    queue.push(run.bind(null, fn, resolve, ...args));

    // 为了保证并发数量能控制准确,要等全部的微任务执行完再拿 activeCount 和 queue.length 来判断
    (async () => {
      await Promise.resolve();

      if (activeCount < concurrency && queue.length > 0) {
        // 如果活跃任务数量小于并发数量,就再跑一个
        queue.shift()();
      }
    })();
  };

  // 返回一个添加并发任务的函数,我们把它叫做 generator。
  // 依旧希望返回返回任务函数的promise的结果
  const generator = (fn, ...args) =>
    new Promise((resolve) => {
      enqueue(fn, resolve, ...args);
    });

  
  return generator;
};
javascript
// 完整版pLimit
// 异步逻辑并行执行,并控制并行数量
// 一个队列来保存任务,有两个时机考虑触发任务执行:
// 1. 开始的时候一次性执行最大并发数的任务 
// 2. 然后每执行完一个启动一个新的
const pLimit = (concurrency) => {
    // 传入并发数量
    if (!((Number.isInteger(concurrency) || concurrency === Infinity) && concurrency > 0)) {
      throw new TypeError('Expected `concurrency` to be a number from 1 and up');
    }
    
    // 添加的并发任务要进行排队,所以我们准备一个 queue
    const queue = [];
    // 记录当前在进行中的异步任务
    let activeCount = 0;
  
    // 下一步处理自然就是把活跃任务数量减一,然后再跑一个任务
    const next = () => {
      activeCount--;
  
      if (queue.length > 0) {
        // 如果队列中还有任务,就再跑一个
        queue.shift()();
      }
    };
  
    // 计数,运行这个函数,改变最后返回的那个 promise 的状态
    const run = async (fn, resolve, ...args) => {
      activeCount++;
      // 运行传入的异步函数
      const result = (async () => fn(...args))();

      resolve(result);
  
      try {
        // 等待异步函数执行完
        await result;
      } catch {}

      // 执行完之后进行下一步处理
      next();
    };
  
    // 把一个异步任务添加到 queue 中,并且只要没达到并发上限就再执行一批任务
    const enqueue = (fn, resolve, ...args) => {
      queue.push(run.bind(null, fn, resolve, ...args));
  
      // 为了保证并发数量能控制准确,要等全部的微任务执行完再拿 activeCount 和 queue.length 来判断
      (async () => {
        await Promise.resolve();
  
        if (activeCount < concurrency && queue.length > 0) {
          // 如果活跃任务数量小于并发数量,就再跑一个
          queue.shift()();
        }
      })();
    };
  
    // 返回一个添加并发任务的函数,我们把它叫做 generator
    const generator = (fn, ...args) =>
      new Promise((resolve) => {
        enqueue(fn, resolve, ...args);
      });

    // 为 generator 添加一些属性,方便外部获取当前的状态
    Object.defineProperties(generator, {
      activeCount: {
        get: () => activeCount
      },
      pendingCount: {
        get: () => queue.length
      },
      // 提供了一个清空任务队列的函数
      clearQueue: {
        value: () => {
          queue.length = 0;
        }
      }
    });
    
    return generator;
  };


// 测试代码
const limit = pLimit(2);
  
function asyncFun(value, delay) {
    return new Promise((resolve) => {
        console.log('start ' + value);
        setTimeout(() => resolve(value), delay);
    });
}

(async function () {
    const arr = [
        limit(() => asyncFun('aaa', 2000)),
        limit(() => asyncFun('bbb', 3000)),
        limit(() => asyncFun('ccc', 1000)),
        limit(() => asyncFun('ccc', 1000)),
        limit(() => asyncFun('ccc', 1000))
    ];
  
    const result = await Promise.all(arr);
    console.log(result);
})();

20. 细胞状态模拟

题目

有 8 个细胞,状态由 0(无活性)或 1(有活性)表示。细胞呈直线排列(注意不是环形!!!)。每天,每个细胞都会与其相邻细胞(邻居)竞争。整数值1代表活跃细胞,0代表不活跃细胞。如果两个邻居同为活跃或不活跃,该细胞第二天就会变为不活跃状态。否则,第二天它就会变为活跃状态。两端的两个细胞只有一个相邻细胞,因此另一个细胞可假定始终为不活跃状态。

示例:

md
8  
1 0 0 0 0 1 0 0  
1

最终状态:[0,1,0,0,1,0,1,0]

更新规则: 每个细胞下一轮的状态,由左右相邻细胞当前状态决定: 若左右状态不同(一个 0、一个 1 )→ 下一轮变为 1(激活)。 若左右状态相同(都是 0 或都是 1 )→ 下一轮变为 0(失活)。 需要模拟 days 天后的细胞状态,输出最终 8 个细胞的 0/1 序列。

模拟细胞每天根据邻居状态变化的过程,本质是多轮的状态迭代问题,核心是根据规则迭代更新细胞状态,类似「生命游戏」简化版,重点在理解规则、正确处理环形邻居,以及保证每轮状态更新的正确性 。

更新规则: 每个细胞下一轮的状态,由左右相邻细胞当前状态决定: 若左右状态不同(一个 0、一个 1 )→ 下一轮变为 1(激活)。 若左右状态相同(都是 0 或都是 1 )→ 下一轮变为 0(失活)。 要求:给定初始状态和天数,模拟迭代 days 次后的最终状态。

输入: 第一行:细胞数量(固定为 8,可忽略或直接用长度 8 处理 )。 第二行:8 个数字,代表细胞初始状态(0/1 序列 )。 第三行:迭代的天数 days。 输出: 迭代 days 次后,8 个细胞的状态(0/1 序列 )

核心逻辑 循环迭代:按天数重复更新细胞状态,每次迭代都要基于上一轮的完整状态计算新状态(不能边更新边覆盖,否则会影响后续细胞判断 )。

邻居逻辑不是环形,是线性! 对于两端细胞(i=0 或 i=n-1): i=0:左邻居视为 0,右邻居是 cells [1] i=n-1:右邻居视为 0,左邻居是 cells [n-2] 中间细胞(0 <i < n-1):左右邻居是 cells [i-1] 和 cells [i+1]

如果是环形邻居处理:用取模 % 或边界判断,让第一个细胞的左邻居是最后一个,最后一个的右邻居是第一个。

状态推导:对每个细胞,比较左右邻居状态,按规则生成新状态。

解法

javascript
function main(input) {
    const lines = input.trim().split('\n').filter(line => line.trim() !== '');
    const cellStatesLine = lines[1]; 
    const daysLine = lines[2]; 

    let cells = cellStatesLine.split(' ').map(Number);
    const days = parseInt(daysLine, 10);
    const n = cells.length;

    for (let d = 0; d < days; d++) {
        const newCells = [];
        for (let i = 0; i < n; i++) {
            let left, right;
            if (i === 0) {
                // 左端细胞:左邻居视为 0,右邻居是 cells[1]
                left = 0;
                right = cells[1];
            } else if (i === n - 1) {
                // 右端细胞:右邻居视为 0,左邻居是 cells[n-2]
                left = cells[n - 2];
                right = 0;
            } else {
                // 中间细胞:左右邻居正常取
                left = cells[i - 1];
                right = cells[i + 1];
            }
            newCells[i] = left !== right ? 1 : 0;
        }
        cells = newCells;
    }

    process.stdout.write(cells.join(' ') + '\n');
}

// 输入监听逻辑(保留)
process.stdin.resume();
process.stdin.setEncoding("utf-8");
var stdin_input = "";

process.stdin.on("data", function (input) {
    stdin_input += input;
});

process.stdin.on("end", function () {
    main(stdin_input);
});


// 输入:
// 8  
// 1 0 0 0 0 1 0 0  
// 1


// 执行流程:
// 细胞 0(i=0):左 = 0,右 = cells [1]=0 → 左右相同 → 0
// 细胞 1(i=1):左 = cells [0]=1,右 = cells [2]=0 → 不同 → 1
// 细胞 2(i=2):左 = cells [1]=0,右 = cells [3]=0 → 相同 → 0
// 细胞 3(i=3):左 = cells [2]=0,右 = cells [4]=0 → 相同 → 0
// 细胞 4(i=4):左 = cells [3]=0,右 = cells [5]=1 → 不同 → 1
// 细胞 5(i=5):左 = cells [4]=0,右 = cells [6]=0 → 相同 → 0
// 细胞 6(i=6):左 = cells [5]=1,右 = cells [7]=0 → 不同 → 1
// 细胞 7(i=7):左 = cells [6]=0,右 = 0 → 相同 → 0

如果是环形排列解法:

javascript
function main(input) {
    // 解析输入:取前 3 行(数量、状态、天数)
    const lines = input.trim().split('\n').filter(line => line.trim() !== '');
    // 第一行是数量(题目中固定为8,可忽略,但需确保第二行是状态)
    const cellStatesLine = lines[1]; 
    const daysLine = lines[2]; 

    let cells = cellStatesLine.split(' ').map(Number);
    const days = parseInt(daysLine, 10);
    const n = cells.length; // 应为 8

    // 逐天更新细胞状态(原逻辑保留)
    for (let d = 0; d < days; d++) {
        const newCells = [];
        for (let i = 0; i < n; i++) {
            const left = cells[(i - 1 + n) % n];
            const right = cells[(i + 1) % n];
            newCells[i] = left !== right ? 1 : 0;
        }
        cells = newCells;
    }

    // 输出最终状态
    process.stdout.write(cells.join(' ') + '\n');
}

// 输入监听逻辑(确保收集完整输入)
process.stdin.resume();
process.stdin.setEncoding("utf-8");
var stdin_input = "";

process.stdin.on("data", function (input) {
    stdin_input += input;
});

process.stdin.on("end", function () {
    main(stdin_input);
});

21. 虚拟内存管理

题目

给定缓存的最大容量和页请求数组,计算缓存未命中的次数。当请求的页在缓存中找不到时,发生缓存未命中。初始时缓存为空 。

操作系统使用先进先出(FIFO)缓存进行虚拟内存管理。当请求的页不在缓存中且缓存已满时,缓存中最久未使用(存在时间最长)的页会被移除,为请求的页腾出空间。若缓存未满,请求的页可直接加入缓存。同一页在缓存中最多出现一次 。

输入说明:

输入第一行是正整数 page_requests_size,表示页请求的总数N。

第二行包含 N 个用空格分隔的正整数 page_requests[0], page_requests[1] ... page_requests[N - 1],代表 N 次页请求。

最后一行是整数 max_cache_size,表示缓存的容量 。

输出说明:

输出一个整数,表示缓存未命中的次数 。

注意事项:

假设 page_requests 数组中的页编号在 1 - 50 范围内。page_requests 中索引为 i 的页,在索引为 i + 1 的页之前被请求 。

示例:

md
6  
1 2 1 3 1 2  
2

输出需按规则计算缓存未命中次数(如前面分析的流程,输出 5 这类结果 )。

整体就是让你用代码实现 FIFO 缓存模拟,统计页请求时的未命中次数,属于经典的 “缓存置换算法模拟” 编程题 。

解法

javascript
function main(input) {
    // 拆分输入为行,过滤空行
    const lines = input.trim().split('\n').filter(line => line.trim() !== '');
    
    // 解析输入(严格按题目要求的三行)
    const pageRequestsSize = parseInt(lines[0], 10);
    const pageRequests = lines[1].split(' ').map(Number);
    const maxCacheSize = parseInt(lines[2], 10);

    // 校验输入长度(可选但推荐)
    if (pageRequests.length !== pageRequestsSize) {
        process.stdout.write("0"); // 输入非法时返回 0(或按题意处理)
        return;
    }

    // FIFO 缓存实现(用数组模拟队列)
    const cache = [];
    let cacheMisses = 0;

    for (const page of pageRequests) {
        if (!cache.includes(page)) {
            cacheMisses++; // 未命中计数
            if (cache.length >= maxCacheSize) {
                cache.shift(); // 缓存满时移除队首(最早进入的页面)
            }
            cache.push(page); // 新增页面到队尾
        }
        // 命中时不处理(FIFO 不调整顺序)
    }

    // 输出结果
    process.stdout.write(cacheMisses.toString());
}

// 输入监听逻辑(保持原样)
process.stdin.resume();
process.stdin.setEncoding("utf-8");
let stdin_input = "";

process.stdin.on("data", (input) => {
    stdin_input += input;
});

process.stdin.on("end", () => {
    main(stdin_input);
});

// 测试代码
// 输入:
// 6
// 1 2 1 3 1 2
// 2

// 执行过程:
// 请求1 → 未命中(缓存: [1])→ 计数1
// 请求2 → 未命中(缓存: [1,2])→ 计数2
// 请求1 → 命中(缓存不变)
// 请求3 → 未命中(缓存满,移除1 → [2,3])→ 计数3
// 请求1 → 未命中(缓存满,移除2 → [3,1])→ 计数4
// 请求2 → 未命中(缓存满,移除3 → [1,2])→ 计数5

// 输出:5(与题目示例一致)