leetcode----JavaScript 详情题解(1)

目录

2618. 检查是否是类的对象实例

2619. 数组原型对象的最后一个元素

2620. 计数器

2621. 睡眠函数

2622. 有时间限制的缓存 

2623. 记忆函数

2625. 扁平化嵌套数组

2626. 数组归约运算

2627. 函数防抖


2618. 检查是否是类的对象实例

请你编写一个函数,检查给定的值是否是给定类或超类的实例。

可以传递给函数的数据类型没有限制。例如,值或类可能是  undefined 。

示例 :

输入:func = () => checkIfInstance(new Date(), Date)
输出:true
解释:根据定义,Date 构造函数返回的对象是 Date 的一个实例。
题解
var checkIfInstanceOf = function(obj, classFunction) {
      if(obj === null || classFunction === null || obj === undefined || classFunction === undefined) {
     return false;
  }
 let proto = Object.getPrototypeOf(obj);
  
  while (proto !== null) {
    if (proto.constructor === classFunction) {
      return true;
    }
    proto = Object.getPrototypeOf(proto);
  }
  
  return false;
};

这段代码定义了一个名为checkIfInstanceOf的函数,该函数的作用是判断一个对象是否是指定类的实例。

函数接受两个参数:obj和classFunction,分别表示待判断的对象和指定类的构造函数。

首先,函数会检查obj和classFunction是否为null或undefined,如果是的话,函数直接返回false,表示对象不是指定类的实例。

接下来,函数会通过Object.getPrototypeOf(obj)获取obj的原型。然后,进入一个while循环,不断获取原型的原型,直到原型为null为止。

在每一次循环中,函数会判断当前原型的构造函数是否等于传入的classFunction。如果是的话,表示obj是classFunction的实例,函数会返回true。

如果循环结束后仍未找到符合条件的原型,函数会返回false,表示obj不是指定类的实例。

最后,整个函数的定义结束。

2619. 数组原型对象的最后一个元素

请你编写一段代码实现一个数组方法,使任何数组都可以调用 array.last() 方法,这个方法将返回数组最后一个元素。如果数组中没有元素,则返回 -1 。

你可以假设数组是 JSON.parse 的输出结果。

示例 :

输入:nums = [null, {}, 3]
输出:3
解释:调用 nums.last() 后返回最后一个元素: 3。

题解:

Array.prototype.last = function() {
   let res=this[this.length-1]
   return res===undefined?-1:res
};

这段代码定义了一个名为last的函数,该函数是Array原型的一个方法。该方法用于获取数组中的最后一个元素。

函数内部首先通过this关键字获取调用该方法的数组对象。然后通过this.length - 1获取数组中最后一个元素的索引,并将其赋值给变量res。

接下来使用三元运算符判断变量res的值是否为undefined。如果是undefined,则说明数组为空,函数返回-1;否则,函数返回变量res的值,即数组中的最后一个元素。

这段代码的作用是为数组对象添加一个方法,方便获取数组的最后一个元素。如果数组为空,则返回-1。

2620. 计数器

请你编写并返回一个 计数器 函数,它接收一个整型参数 n 。这个 计数器 函数最初返回 n,每次调用它时返回前一个值加 1 的值 ( n ,  n + 1 ,  n + 2 ,等等)。

示例 :

输入:
n = 10 
["call","call","call"]
输出:[10,11,12]
解释:
counter() = 10 // 第一次调用 counter(),返回 n。
counter() = 11 // 返回上次调用的值加 1。
counter() = 12 // 返回上次调用的值加 1。

题解:

var createCounter = function (n) {
    return function () {
        return n++;
    };
};

  • var createCounter 定义了一个变量 createCounter,它将引用这个函数。
  • function (n) 是一个匿名函数,它接受一个参数 n
  • return function () { ... } 是匿名函数的返回值,它也是一个函数。
  • return n++ 是返回了变量 n 的值,并将 n 自增 1。

因此,当调用 createCounter 函数时,它会返回一个新的函数。每次调用这个新函数时,它会返回变量 n 的当前值,并将 n 自增 1。这样就创建了一个简单的计数器,每次调用计数器函数时,计数器的值都会增加。

2621. 睡眠函数

请你编写一个异步函数,它接收一个正整数参数 millis ,并休眠这么多毫秒。要求此函数可以解析任何值。

示例

输入:millis = 100
输出:100
解释:
在 100ms 后此异步函数执行完时返回一个 Promise 对象
let t = Date.now();
sleep(100).then(() => {
  console.log(Date.now() - t); // 100
});

题解

async function sleep(millis) {
    return new Promise((reslove, reject) => {
        setTimeout(() => {
            reslove()
        }, millis)
    })
}

这是一个异步 JavaScript 函数,名称为 sleep。这个函数接受一个参数 millis,用来标明延迟的毫秒数。

这个函数的功能是创建一个新的 Promise 对象,这个 Promise 对象在经过指定的毫秒数 millis 后解决(即执行 resolve 方法)。由于 JavaScript 的 Timer 函数(例如 setTimeout)是非阻塞的,sleep 函数使用 Promise 和 setTimeout 一起来实现一个"暂停"功能。

当你在一个 async 函数中使用 await sleep(millis) 形式调用此函数时,该函数会“暂停”或者说“睡眠”指定的毫秒数,然后继续执行。这样的函数在需要延迟一段时间然后执行的场景中特别有用。

2622. 有时间限制的缓存 

编写一个类,它允许获取和设置键-值对,并且每个键都有一个 过期时间 。

该类有三个公共方法:

set(key, value, duration) :接收参数为整型键 key 、整型值 value 和以毫秒为单位的持续时间 duration 。一旦 duration 到期后,这个键就无法访问。如果相同的未过期键已经存在,该方法将返回 true ,否则返回 false 。如果该键已经存在,则它的值和持续时间都应该被覆盖。

get(key) :如果存在一个未过期的键,它应该返回这个键相关的值。否则返回 -1 。

count() :返回未过期键的总数。

示例

["TimeLimitedCache", "set", "get", "count", "get"]
[[], [1, 42, 100], [1], [], [1]]
[0, 0, 50, 50, 150]
输出: [null, false, 42, 1, -1]
解释:
在 t=0 时,缓存被构造。
在 t=0 时,添加一个键值对 (1: 42) ,过期时间为 100ms 。因为该值不存在,因此返回false。
在 t=50 时,请求 key=1 并返回值 42。
在 t=50 时,调用 count() ,缓存中有一个未过期的键。
在 t=100 时,key=1 到期。
在 t=150 时,调用 get(1) ,返回 -1,因为缓存是空的。

var TimeLimitedCache = function () {
    this.map = new Map();
    this.timeoutMap = new Map();
};

TimeLimitedCache.prototype.set = function (key, value, duration) {
    if (this.timeoutMap.has(key)) {
        clearTimeout(this.timeoutMap.get(key));
    }
    let isKeyExists = this.map.has(key);
    this.map.set(key, value);
    this.timeoutMap.set(key, setTimeout(() => {
        this.map.delete(key);
        this.timeoutMap.delete(key);
    }, duration));

    return isKeyExists;
};


TimeLimitedCache.prototype.get = function (key) {
    if (this.map.has(key)) {
        return this.map.get(key);
    } else {
        return -1;
    }
};


TimeLimitedCache.prototype.count = function () {
    return this.map.size;
};

这段代码定义了一个名为TimeLimitedCache的构造函数。构造函数内部有两个实例属性,map和timeoutMap,分别使用JavaScript内置的Map类来存储数据。

构造函数之后定义了三个方法。

  1. set方法用于向缓存中设置一个键值对,并指定过期时间。如果指定键已存在,且仍未过期,set方法会清除之前的过期计时器。然后,它会将键值对存储到map中,并使用setTimeout方法设置过期计时器,计时器到期后会从map和timeoutMap中删除该键值对。返回值表示之前是否已存在未过期的键。

  2. get方法用于根据键获取对应的值。如果键存在于map中,则返回对应的值;否则,返回-1。

  3. count方法用于返回当前未过期的键的数量,即map的大小。

这段代码实现了一个有时限的缓存机制,可以设置过期时间,并能够根据键获取对应的值以及获取未过期键的数量。

2623. 记忆函数

请你编写一个函数,它接收另一个函数作为输入,并返回该函数的 记忆化 后的结果。

记忆函数 是一个对于相同的输入永远不会被调用两次的函数。相反,它将返回一个缓存值。

你可以假设有 3 个可能的输入函数:sum 、fib 和 factorial 。

  •  sum 接收两个整型参数 a 和 b ,并返回 a + b 。
  •  fib 接收一个整型参数 n ,如果 n <= 1 则返回 1,否则返回 fib (n - 1) + fib (n - 2)
  •  factorial 接收一个整型参数 n ,如果 n <= 1 则返回  1 ,否则返回 factorial(n - 1) * n 。

示例

输入:
"sum"
["call","call","getCallCount","call","getCallCount"]
[[2,2],[2,2],[],[1,2],[]]
输出:
[4,4,1,3,2]

解释:
const sum = (a, b) => a + b;
const memoizedSum = memoize(sum);
memoizedSum (2, 2);// 返回 4。sum() 被调用,因为之前没有使用参数 (2, 2) 调用过。
memoizedSum (2, 2);// 返回 4。没有调用 sum(),因为前面有相同的输入。
//总调用数: 1
memoizedSum(1、2);// 返回 3。sum() 被调用,因为之前没有使用参数 (1, 2) 调用过。
//总调用数: 2

题解 

function memoize(func) {
  var cache = {};

  return function(...args) {
    var key = JSON.stringify(args);
    if (cache.hasOwnProperty(key)) {
      return cache[key];
    }
    
    var result = func(...args);
    cache[key] = result;
    return result;
  };
}

这段代码定义了一个名为memoize的函数,它接受一个参数func。memoize函数返回一个新的函数,并利用闭包在返回的函数中创建一个缓存对象cache。

返回的函数接受任意数量的参数,并将这些参数使用JSON.stringify方法转换成一个字符串作为缓存字典cache的键。如果缓存对象已经存在该键,则直接返回缓存中的值。

如果缓存对象中不存在该键,那么调用传入的原始函数func,将其返回值存储在result变量中。并将结果存储在缓存对象的对应键中。最后,返回结果。

这段代码的作用是:在调用原始函数之前,先检查是否已经存在相同参数的结果。如果存在,则直接返回缓存中的结果,避免重复计算。如果不存在,则计算参数对应的结果,并将结果存储在缓存中,以备将来使用。

2625. 扁平化嵌套数组

请你编写一个函数,它接收一个 多维数组 arr 和它的深度 n ,并返回该数组的 扁平化 后的结果。

多维数组 是一种包含整数或其他 多维数组 的递归数据结构。

数组 扁平化 是对数组的一种操作,定义是将原数组部分或全部子数组删除,并替换为该子数组中的实际元素。只有当嵌套的数组深度大于 n 时,才应该执行扁平化操作。第一层数组中元素的深度被认为是 0。

请在没有使用内置方法 Array.flat 的前提下解决这个问题。

示例

输入
arr = [1, 2, 3, [4, 5, 6], [7, 8, [9, 10, 11], 12], [13, 14, 15]]
n = 0
输出
[1, 2, 3, [4, 5, 6], [7, 8, [9, 10, 11], 12], [13, 14, 15]]

解释
传递深度 n=0 的多维数组将始终得到原始数组。这是因为 子数组(0) 的最小可能的深度不小于 n=0 。因此,任何子数组都不应该被平面化。

题解


var flat = function (arr, n) {
    if (n === 0) {
        return arr;
    }
    const res = flat(arr, n - 1);
    return [].concat(...res);
};

这段代码的作用是将一个嵌套的数组(arr)展开成一个一维数组,展开的层数由参数n决定。

代码的流程是:

  1. 定义一个名为flat的函数,接受两个参数,一个是要展开的数组arr,另一个是展开的层数n。

  2. 如果n等于0,表示已经展开完毕,直接返回原始数组arr。

  3. 如果n不等于0,则递归调用flat函数,将展开的层数减1,得到一个新的数组res。

  4. 使用数组的concat方法将新的数组res拼接成一个一维数组,并返回该数组。

这个函数的实现原理是通过递归的方式,每次递归都将展开的层数减1,直到达到指定的展开层数,然后通过concat方法将递归得到的中间展开结果合并成最终结果。

2626. 数组归约运算

归约

在计算机科学中,归约(reduction)通常指的是将一个问题或数据集合转化为较小的、等价的形式的过程。归约可以被用于解决算法问题、优化计算和简化复杂性。

在算法问题中,归约通常用于将一个复杂的问题转化为一个更简单的问题。这个过程通常通过分解问题、提取共性和重复性的步骤来实现。通过将复杂问题分解为更小的子问题,然后将结果合并,最终解决整个问题。

归约的目标是通过将问题拆分成更小的、可处理的部分来提高求解的效率。它可以用于各种算法和数据结构问题,如排序、搜索、图形算法和动态规划等。通过归约,我们可以将复杂度较高的问题转化为具有更低复杂度的子问题,并通过递归或迭代的方式求解最终答案。

总的来说,归约是将一个问题或数据集合转化为更小、更简单、等价的形式的过程,以便更高效地解决问题。它是计算机科学中一种常见的问题求解方法和算法设计技巧。


你编写一个函数,它的参数为一个整数数组 nums 、一个计算函数 fn 和初始值 init 。返回一个数组 归约后 的值。

你可以定义一个数组 归约后 的值,然后应用以下操作: val = fn(init, nums[0]) , val = fn(val, nums[1]) , val = fn(val, nums[2]) ,... 直到数组中的每个元素都被处理完毕。返回 val 的最终值。

如果数组的长度为 0,它应该返回 init 的值。

请你在不使用内置数组方法的 Array.reduce 前提下解决这个问题。

示例

输入:
nums = [1,2,3,4]
fn = function sum(accum, curr) { return accum + curr; }
init = 0
输出:10
解释:
初始值为 init=0 。
(0) + nums[0] = 1
(1) + nums[1] = 3
(3) + nums[2] = 6
(6) + nums[3] = 10
Val 最终值为 10。

题解 

var reduce = function (nums, fn, init) {
    if (nums.length === 0) { return init; }

    let val = init;
    for (let i = 0; i < nums.length; i++) {
        val = fn(val, nums[i]);
    }

    return val;
};

这段代码定义了一个名为reduce的函数,该函数接受三个参数:nums(数组)、fn(回调函数)和init(初始值)。

函数的作用是将数组中的每个元素都传递给回调函数进行处理,并返回最终的计算结果。

首先,函数会判断数组的长度是否为0,若是则直接返回初始值。

接下来,定义一个变量val并将其初始化为初始值。

然后,通过一个for循环遍历数组中的每个元素,每次循环都将当前元素和val作为参数传递给回调函数fn进行处理,得到的结果赋值给val。

最后,返回最终计算得到的val。

2627. 函数防抖

请你编写一个函数,接收参数为另一个函数和一个以毫秒为单位的时间 t ,并返回该函数的 函数防抖 后的结果。

函数防抖 方法是一个函数,它的执行被延迟了 t 毫秒,如果在这个时间窗口内再次调用它,它的执行将被取消。你编写的防抖函数也应该接收传递的参数。

例如,假设 t = 50ms ,函数分别在 30ms 、 60ms 和 100ms 时调用。前两个函数调用将被取消,第三个函数调用将在 150ms 执行。如果改为 t = 35ms ,则第一个调用将被取消,第二个调用将在 95ms 执行,第三个调用将在 135ms 执行。

leetcode----JavaScript 详情题解(1)_第1张图片

上图展示了了防抖函数是如何转换事件的。其中,每个矩形表示 100ms,反弹时间为 400ms。每种颜色代表一组不同的输入。

请在不使用 lodash 的 _.debounce() 函数的前提下解决该问题。

示例

输入:
t = 50
calls = [
  {"t": 50, inputs: [1]},
  {"t": 75, inputs: [2]}
]
输出:[{"t": 125, inputs: [2]}]
解释:
let start = Date.now();
function log(...inputs) { 
  console.log([Date.now() - start, inputs ])
}
const dlog = debounce(log, 50);
setTimeout(() => dlog(1), 50);
setTimeout(() => dlog(2), 75);

第一次调用被第二次调用取消,因为第二次调用发生在 100ms 之前
第二次调用延迟 50ms,在 125ms 执行。输入为 (2)。

题解

function debounce(func, t) {
  let timeout;
  
  return function (...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(this, args);
    }, t);
  };
}

这个函数接受两个参数:func 表示要进行函数防抖的函数,t 表示延迟的时间窗口。

在内部,我们定义了一个变量 timeout 用于存储定时器的标识。然后,返回一个立即执行函数,这个函数接受参数 args 作为传递给被防抖的函数 func 的参数。

每当调用该函数时,我们首先清除之前的定时器,然后设置一个新的定时器,在延迟时间 t 后调用函数 func。这样,如果在延迟时间窗口内再次调用该函数,之前的定时器将被清除,然后重新设置一个新的定时器。

这样就实现了函数防抖:只有当在延迟时间窗口内没有再次调用该函数时,才会真正执行函数 func。如果在延迟时间窗口内再次调用该函数,上一个定时器会被取消,因此函数不会被执行。

❤❤❤❤❤❤------this------❤❤❤❤❤❤

在函数防抖的实现中,使用func.apply(this, args)来调用被防抖的函数func,其中this的值是由调用防抖函数的上下文确定的。

当使用箭头函数创建防抖函数时,箭头函数没有独立的执行上下文,它会继承上一级作用域的this值,因此在防抖函数中,this的值将与上一级作用域中的this相同。

例如,在全局作用域中调用防抖函数时,防抖函数中的this将指向全局对象(浏览器中是window对象):

function debounce(func, t) {
  let timeout;
  
  return function (...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(this, args);
    }, t);
  };
}

function example() {
  console.log(this); // global object (e.g. window)
}

const debouncedExample = debounce(example, 500);
debouncedExample();

而当在对象的方法中调用防抖函数时,防抖函数中的this将指向该对象:

const obj = {
  name: 'example',
  debouncedMethod: debounce(function () {
    console.log(this.name); // example
  }, 500)
};

obj.debouncedMethod();

总的来说,防抖函数内部的this指向是由调用防抖函数的上下文决定的,可以通过箭头函数继承上一级作用域的this值,或者使用applycall等方法来显式指定this的值。

你可能感兴趣的:(leetcode,算法,职场和发展)