1. 写一个 mySetInterVal(fn, a, b),每次间隔 a,a+b,a+2b 的时间,然后写一个 myClear,停止上面的 mySetInterVal
思路
- 从时间间隔 a, a+b, a+2b... 可以找到规律就是第一次之后每次执行时间都是多一个 b.
- 使用 setTimeout 定时器,时间到了执行一个函数,并且继续开启一个定时器时间加为 b。
- 由于第一次时间是基础,往后都是多一个 b,只要区分是否是第一次执行就可以了。
- 核心思路就是不断重复定时器>触发函数+开启一个新定时器,使用一个标志区分是否第一次。
代码实现
function Fn(fn, a, b) {
// 是否第一次的标识
this.ifFirst = true;
this.timer = null;
Fn.prototype.start = () => {
// 第一次定时器使用 a,往后都是 b
let time = this.ifFirst? a: b;
this.ifFirst = false;
this.timer = setTimeout(() => {
fn();
this.start();
}, time)
}
Fn.prototype.clear = () => {
clearTimeout(this.timer)
}
}
function say(){
console.log(999)
}
let obj = new Fn(say, 3000, 1000);
obj.start();
setTimeout(() => {
obj.clear();
}, 10000)
2. 合并二维有序数组成一维有序数组
这种题肯定不是考察你库函数的用法:concat、sort 的用法,一般都会要求时间复杂度为O(n)
思路
- 因为已知两个数组都是有序,可以直接用双指针从分别从两个数组头部开始遍历,创建一个结果数组 result, 比较指针对应位置的大小,小的写入 result, 移动对应指针,不断重复以上步骤
- 当某个指针遍历完,没走完的指针对应的值都是比之前的大,继续遍历,放入 result;
代码实现
function fn(arr1, arr2) {
let pointer1 = 0, pointer2 = 0;
let result = [];
while (arr1[pointer1] && arr2[pointer2]){
let val1 = arr1[pointer1], val2 = arr2[pointer2];
if(val1 < val2){
result.push(val1);
pointer1++
}else{
result.push(val2);
pointer2++
}
}
return result.concat(arr1.slice(pointer1), arr2.slice(pointer2));
}
console.log(fn([1,3,6,9], [1,3,6,9]))
3.斐波那契数列
3-1. 求包含指定参数的斐波那契数列
这个很简单,就是一个循环,不断往数组添加就好了
function fn(n) {
let result = [1, 1];
if(n === 1){
return [1]
}else if(n === 2){
return [1,1]
}
for(let i=2;i<n;i++){
result[i] = result[i-1] + result[i-2]
}
return result
}
console.log(fn(5))
3-2. 求斐波那契数列第n位是多少
最简单的写法就是递归了,求第n个就是求前一个加前前一个
function fn(n) {
if(n === 1 || n === 2){
return 1;
}
return fn(n-1) + fn(n-2)
}
console.log(fn(5))
上面的方式时间复杂度太高了, 1 + 2 + 4 + 8 ...(2n-1)。时间复杂度到了指数阶。现在做法是求n,就先求n-1,和n-2;那么我直接从1求到n不是就可以了吗。
function fn(n, pre = 1, cur = 1) {
if(n === 1 || n === 2){
return cur;
}
[pre, cur] = [cur, pre + cur]
return fn(n-1, pre, cur )
}
console.log(fn(6))
4.字符串出现的不重复最长长度
思路
- 要查找的一段字符串肯定是连续的,可以使用如图的窗口方式
- 遍历数组,当当前值在窗口中存在时,如果当前窗口长度大于之前保存的 maxLen,更新maxLen,并且移动左指针到重复值之后。
- 最后判断一下当前窗口长度和maxLen,返回大者
function fn(str) {
let maxLen = 0;
let leftIndex = 0;
for(let i=0;i<str.length;i++){
let preStr = str.slice(leftIndex, i);
let preLen = preStr.length;
let cur = str[i];
let preIndex = preStr.indexOf(cur);
if(preIndex > -1 ){
if(preLen>maxLen){
maxLen = preLen;
}
// 注意这里取值
leftIndex = i - preLen + preIndex + 1
}
}
return str.slice(leftIndex).length > maxLen ? str.slice(leftIndex).length : maxLen
}
console.log(fn('abcdabef'))
reduce写法如下,思路相同
function fn(str) {
let arr = [...str];
let maxLen = 0
let tempArr = arr.reduce((result, cur, index) => {
let hasIndex = result.indexOf(cur);
if(hasIndex>-1){
maxLen = result.length>maxLen ? result.length : maxLen;
result = result.slice(hasIndex+1) + cur;
}else{
result += cur
}
return result
})
return tempArr.length>maxLen ? tempArr.length : maxLen
}
console.log(fn('abcdabe'))
5.介绍防抖节流原理、区别以及应用,并用JavaScript进行实现
- 防抖节流都使用了闭包,在函数a外部调用函数内部的函数b,b函数执行时候会获取保存在a里面的变量(这些变量一直都在)做相应的处理。
- 防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时;一般用于点击提交时候防止重复提交和输入搜索只执行最后一次
- 节流:在事件被触发n秒内只执行一次,如果在这n秒内又被触发,不予处理;一般用于处理拖拽和窗口缩放
5-1. 防抖
思路
- debonce函数里面定义 timer 记录定时器。
- 执行内部函数时候先清空定时器(不判断timer存在与否清空也没问题)。
- 如果是立即执行,判断定时器是不是为空,为空说明是第一次进来或是当前操作距上一次够久了,执行执行指定函数,开启定时器,定时器到时定时器赋值为空
- 不是立即执行,直接开启定时器,在定时器到时执行指定函数。
代码实现
function debonce(fn, wait, immediate) {
let timer = null;
return function () {
let content = this;
let args = arguments;
timer && clearTimeout(timer);
if(immediate){
if(!timer){
fn.apply(content, args)
}
timer = setTimeout(() => {
timer = null
}, wait)
}else{
timer = setTimeout(() => {
fn.apply(content, args)
})
}
}
}
这里注意,clearTimeout()后,timer还是有值的。立即执行时,定时器到时需要置空处理。
content 变量和 args 变量要看你定义函数是如何的在使用,像上面这个我都是剪头函数,其实可以不用。
5-2. 节流
思路
- 这个可以分为定时器版本和计时版本
- 定时器版:如果定时器为空,设置定时器,定时器到时重置定时器为空并且执行相应函数。
- 计时版:调用函数时候当前时间和上一回执行时间做对比,如果大于指定时间执行。
定时器版实现
function throttle(fn, wait) {
let timer = null;
return function () {
if(!timer){
fn.apply(this, arguments)
}
timer = setTimeout(() => {
timer = null
}, wait)
}
}
计时器版实现
function throttle(fn, wait) {
let preTime = 0;
return function () {
let curTime = new Date().getTime();
if(curTime - preTime > wait){
preTime = curTime;
fn.apply(this, arguments)
}
}
}
// 使用
let throttleFn = throttle(function () {
}, 100)
6. 判断一个数是否是快乐数
快乐数:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。
思路
- 这里肯定是使用循环不断判断平方和是不是等于1,还需要保存当前值,因为如果出现了当前值,那就说明开始不断的重复了,这是就不应该继续循环了,直接判断那个值是不是符合。
- 要快速定位到使用过某个值,就可以使用set、map、对象来记录使用过的值。
function getN(n) {
let res = 0;
while (n){
res += (n%10) * (n%10);
n = Math.floor(n/10);
}
return res;
}
function isHappy(n) {
let set = new Set();
while (n!==1 && !set.has(n)){
set.add(n);
n = getN(n);
}
return n === 1
}
console.log(isHappy(10))
7. 给定数组查找两个数之和等于指定数
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
思路
- 要查找是否存在,那么遍历数组时候,查找
arr[i]
前面是否存在target - arr[i]
。 - 要想快速定位某个值,就可以使用set、map、对象来记录使用过的值。
- 这里要返回值对应的索引,那么可以使用map。
代码实现
function fn(arr, target) {
let map = new Map();
for(let i=0;i<arr.length;i++){
let need = target - arr[i];
if(map.has(need)){
return [map.get(need), i]
}else{
map.set(arr[i], i)
}
}
return false
}
console.log(fn([1,2,3,4,5], [5]))
这个代码实现的是找到第一个就返回,如果需要做找到所有,就价格数组存放就好了。