看了涡流大佬的面试文章的总结(手撕代码 & 算法)

简介: 看了涡流大佬的面试文章的总结(手撕代码&算法)

手写深拷贝


function deepClone(obj) {
  let newObj = {};
  if(typeof obj !== "object" || obj == null) {
    return obj;
  }
  for(let key in obj) {
    if(typeof obj[key] !== "object") { // 基本类型加function
      newObj[key] = obj[key];
    }else {
      if(isType(obj[key]) == "Array") { // 判断数组
        let newArray = [];
        for(let index in obj[key]) { // 递归调用,然后需要返回当前非对象的值,然后加入
          newArray[index] = deepClone(obj[key][index])
        }
        newObj[key] = newArray;
      }else { // 对象类型
        let newObject = {};
        for(let newKey in obj[key]) {
          newObject[newKey] = deepClone(obj[newKey]);
        }
      }
    }
  }
  return newObj;
}
function isType(value) {
  const type = Object.prototype.toString.call(value);
  switch (type) {
    case "[object Array]":
      return "Array"
    case "[object Object]":
      return "Object"
    default:
      return typeof value;
  }
}


以前写过一篇文章


手写节流和防抖


以前写过一篇文章还是比较全的,感谢coderwhy老师


手写call / apply / bind


简单的实现


  • 思路就是获取当前调用的函数,然后将其作为当前指定的this属性值。为了避免覆盖当前this对象的已有属性,那么将设置symbol值作为属性名。其他的都是一些边界判断。


const myCall = function (context, ...args) {
      let func = this
      context = context ||  window
      if (typeof func !== 'function') throw new TypeError('this is not a function')
      let caller = Symbol('caller')
      context[caller] = func
      let res = context[caller](...args)
      delete context[caller]
      return res
    }
    Function.prototype.myCall = myCall


function myApply(context, arr) {
      let func = this;
      context = context || window;
      if (typeof func !== 'function') throw new TypeError('this is not a function')
      //唯一的键值
      let caller = Symbol('caller')
      context[caller] = func
      //函数返回值
      let res;
      if (!arr) {
        res = context[caller]()
      } else {
        res = context[caller](...arr)
      }
      //删除该函数
      delete context[caller]
      return res
    }
Function.prototype.myApply = myApply;


function myBind(context, ...args) {
  const fn = this;
  if(typeof fn !== "function") throw new TypeError("this is not function");
  context = context || window;
  // 因为bind需要返回一个函数,然后利用call、apply改变this即可。并传入参数。
  return function(...args2){
    fn.call(context, ...args,...args2)
  };
}
Function.prototype.myBind = myBind


手写Promise.all / Promise.race / Promise.allSettled


完整的promise实现,请看以前写的一篇文章


实现它,我们必须的知道他们的作用是啥?


  • all:如果传入的promises全部都变成fulfilled,那么promise的状态将变为fulfilled。如果有一个promises状态变为rejected,那么promsie状态将变为rejected。


function all(promises) {
  return new Promise((resolve, reject) => {
    let resArr = [];
    for(let promise of promises) {
      promise.then(res => { 
        resArr.push(res);
        if(resArr.length === promises.length) { // 每次成功都需要判断
          resolve(resArr)
        }
      })
      promise.catch((err) => {
        reject(err)
      })
    }
  })
}


  • race: 传入的promises最先改变状态的promise决定当前的promise状态。


function race(promises) {
  return new Promise((resolve, reject) => {
    for(let promise of promises) {
      promise.then(res => { 
        resolve(res)
      })
      promise.catch((err) => {
        reject(err)
      })
    }
  })
}


  • allSettled: 当传入的promises的状态全部都变化后,promise的状态变为fulfilled。


function allSettled(promises) {
      return new Promise((resolve) => {
        let resArr = []
        for(let promise of promises) {
          promise.then(res => { 
            resArr.push({
              status: "fulfilled",
              value: res
            })
            if(resArr.length === promises.length) {
              resolve(resArr)
            }
          })
          promise.catch((err) => {
            resArr.push({
              status: "rejected",
              value: err
            })
            if(resArr.length === promises.length) { // 可能promise有异步任务,所以每一次状态改变都需要判断
              resolve(resArr)
            }
          })
        }
      })
    }


手写括号匹配


不知道是不是LeetCode-20。如果是的话,这一题使用栈的思想最好理解。


以前写过一篇文章


数组去重


  • 通过set特点实现。


// set数据结构
    Array.from(new Set(arr))
    [...new Set(arr)]


  • 通过对象键名不能重复实现。


// 利用对象的键不能重复的特点
    function removeDup(arr) {
      const obj = {}
      arr.forEach((item) => {
        if(!obj[item]) {
          obj[item] = true
        }
      })
      return Object.keys(obj)
    }


将奇数排在前面,偶数排在后面。要求时间复杂度O(n)。空间复杂度O(1)(不能用splice)


不知道这一题是不是这个意思。


function oddEven(arr) {
  // 先排序,这个排序算法可以自己去查询时间复杂度为o(n)的算法。
  arr = arr.sort((a, b) => {
    return a - b;
  })
  const odd = []; // 奇数
  const even = []; // 偶数
  for(let item of arr) {
    if(item % 2 === 0) { // 偶数
      even.push(item)
    }else { // 奇数
      odd.push(item)
    }
  }
  return [...odd, ...even]
}


解析出URL中所有的部分


通过URL类解析出来的是这个样子,我们只需要处理一下searchParams和protocol即可


网络异常,图片无法展示
|


function parseUrl(url) {
  const resUrlObj = new URL(url);
  // console.log(resUrlObj)
  const params = {}
  const resObj = {};
  // 处理查询参数
  for(let [key, value] of resUrlObj.searchParams) {
    params[key] = value
  }
  for(let key in resUrlObj) {
    if(key == "protocol") { // 处理协议
      resObj[key] = resUrlObj[key].slice(0, (resUrlObj[key].length - 1))
    }else {
      resObj[key] = resUrlObj[key]
    }
  }
  return {...resObj, searchParams: params}
}


实现一个compare函数,比较两个对象是否相同


这里的比较应该不是值广义的地址,而是指对象的属性和值。


function compare(obj1, obj2) {
  const obj1Keys = Object.keys(obj1);
  const obj2Keys = Object.keys(obj2);
  if(obj1Keys.length !== obj2Keys.length) return false;
  for(let key of obj1Keys) {
    if(!obj2Keys.includes(key)) {
      return false;
    }else {
      if(typeof obj1[key] === "object" || typeof obj2[key] === "object") { // 递归调用,
        // 注意:这里也可以判断数组,Object.keys([1,2,3]) => ["0", "1", "2"]
        if(!compare(obj1[key], obj2[key])) return false;
      } else {// 基本类型
        if(obj1[key] !== obj2[key]) return false;
      }
    }
  }
  return true;
}


柯里化


是把接收多个参数的函数,变成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数,而且返回结果的新函数的技术。


实现思路


  • 我们知道函数的length属性是判断形参的个数。


  • 判断不断调用的函数传入的中参数是否大于等于函数的形参。


  • 如果大于等于,就直接调用该函数,并传入参数


  • 如果小于,那就再返回一个函数,并且内部返回递归调用的函数。


function curry(fn, ...args1) {
      // 获取形参的个数
      const length = fn.length;
      const resFn = function(...args2) {
        const totalArgs = [...args1, ...args2];
        if(totalArgs.length >= length) {
          // return resFn;
          fn.call(this, ...totalArgs)
        }else { // 参数还没有传完
          return function(...args3) {
            return resFn.call(this, ...args2, ...args3)
          }
        }
      }
      return resFn
    }


中划线转大写


function lineChangeUpperCase(str) {
      // 先依据_分割字符串
      const strArr = str.split("_");
      // 利用map映射将每个短横线后面的字符转大写
      return strArr.map((item, index) => {
        if(index === 0) {
          return item;
        }else {
          return item.slice(0, 1).toUpperCase() + item.slice(1)
        }
      }).join("")
    }


千位分割


以前写过一篇文章


使用es5实现es6的let关键字


利用自执行函数


// 下面代码不存在块级作用域
    for(var i = 0; i < 5; i++) {
      setTimeout(() => {
        console.log(i); //  5  5  5  5  5
      }, 0)
    }
    // let解决的问题
    for(let i = 0; i < 5; i++) {
      setTimeout(() => {
        console.log(i); //  0 1 2 3 4 
      }, 0)
    }
    // 立即执行函数代替let
    for(var i = 0; i < 5; i++){
      (function(a){
        setTimeout(() => {
          console.log(a); //  0 1 2 3 4 
        }, 0)
      })(i)
    }


查找数组中的重复元素


  • 双重for循环


function getRepeatElement(arr) {
      let resArr = []
      for(let i = 0; i < arr.length; i++) {
      // for(let i  in arr) { // 为什么for in 不正确
        for( let j = i + 1; j < arr.length; j++) {
          if(arr[i] == arr[j]) {
            resArr.push(arr[i])
          }
          continue;
        }
      }
      return resArr 
    }


  • 数组中判断数组中方法(includes, indexOf)实现。


// indexOf / includes实现
    function getRepeatElement1(arr) {
      let resArr = []
      for(let i = 0; i < arr.length; i++) {
        let copyArr = [...arr]
        let item = arr[i];
        copyArr.splice(i, 1);
        if(copyArr.includes(item)) {
        // if(copyArr.indexOf(item) !== -1) {
          resArr.push(item)
        }
      }
      return resArr //[5, 3, 4, 4]
    }


  • 先进行排序,然后再通过比较相邻元素的方式。


// 先排序,然后再比较相邻元素。
    function getRepeatElement3(arr) {
      let resArr = []
      copyArr = arr.sort();
      for(let i = 0; i < arr.length - 1; i++) {
        if(arr[i] === arr[i + 1]) {
          resArr.push(arr[i]);
        }
      }
      return resArr
    }


数组乱序


  • 通过sort结合Math.random()。


// 该方法有缺陷,大多数元素的位置是不变的
    function mixArr(arr) {
      return arr.sort(() => Math.random() - 0.5)
    }


  • 洗牌算法


// 洗牌算法
    function shunflee(arr) {
      const len = arr.length
      while(len > 1) {
        const index = parsetInt(Math.random() * len--)
        [arr[index], arr[len]] = [arr[len], arr[index]]
      }
      return arr
    }


相关文章
|
10天前
|
算法
分享一些提高二叉树遍历算法效率的代码示例
这只是简单的示例代码,实际应用中可能还需要根据具体需求进行更多的优化和处理。你可以根据自己的需求对代码进行修改和扩展。
|
1月前
|
算法 Java 数据库
美团面试:百亿级分片,如何设计基因算法?
40岁老架构师尼恩分享分库分表的基因算法设计,涵盖分片键选择、水平拆分策略及基因法优化查询效率等内容,助力面试者应对大厂技术面试,提高架构设计能力。
美团面试:百亿级分片,如何设计基因算法?
|
1月前
|
算法 前端开发 Java
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
这篇文章总结了单链表的常见面试题,并提供了详细的问题分析、思路分析以及Java代码实现,包括求单链表中有效节点的个数、查找单链表中的倒数第k个节点、单链表的反转以及从尾到头打印单链表等题目。
33 1
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
|
21天前
|
算法 测试技术 开发者
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗;代码审查通过检查源代码发现潜在问题,提高代码质量和团队协作效率。本文介绍了一些实用的技巧和工具,帮助开发者提升开发效率。
25 3
|
20天前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
29天前
|
机器学习/深度学习 算法 Java
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
|
1月前
|
存储 缓存 算法
如何通过优化算法和代码结构来提升易语言程序的执行效率?
如何通过优化算法和代码结构来提升易语言程序的执行效率?
|
1月前
|
搜索推荐
插入排序算法的讲解和代码
【10月更文挑战第12天】插入排序是一种基础的排序算法,理解和掌握它对于学习其他排序算法以及数据结构都具有重要意义。你可以通过实际操作和分析,进一步深入了解插入排序的特点和应用场景,以便在实际编程中更好地运用它。
|
1月前
|
算法 Java 数据库
美团面试:百亿级分片,如何设计基因算法?
40岁老架构师尼恩在读者群中分享了关于分库分表的基因算法设计,旨在帮助大家应对一线互联网企业的面试题。文章详细介绍了分库分表的背景、分片键的设计目标和建议,以及基因法的具体应用和优缺点。通过系统化的梳理,帮助读者提升架构、设计和开发水平,顺利通过面试。
美团面试:百亿级分片,如何设计基因算法?
|
1月前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
72 2