常见js手撕题及算法总结

文章目录

            • 实现一下观察者模式
            • 实现一下工厂模式
            • 实现一下单例模式
            • 设计一个lazyMan,实现以下功能:
            • 实现一个简单的EventEmitter
            • 手撕实现原生js的一些方法(call、apply、bind、map、reduce、filter、splice、pop、push、unshift、shift)
            • 手撕实现一下前端路由
            • 简单实现一下实现事件委托(ul中很多个li标签)
            • 用setTimeOut实现一下setInterval
            • 实现一下curry函数(柯里化函数)
            • 实现类似 add(1)(2) add(1, 2, 3)(10) add(1)(2)(3)(4)(5)的函数
            • 实现一下es5的几种继承方法
            • 代码实现一下new方法过程
            • 简单实现promise方法
            • 实现一个promise失败重传函数(重传限定多少次)
            • 实现一个基于promise的ajax函数
            • 实现一个深拷贝函数
            • 用函数实现一下instanceof的功能
            • 实现防抖和节流函数
            • 实现数组扁平化函数flatten
            • 创建形如a.b.c.d嵌套对象
            • 二叉树的前中后序(深度优先)及层序(广度优先)遍历
            • 实现以下lodash的_get()函数
            • 实现将url中?后的参数转化为js对象
            • 实现大数相加
            • 字符串的全排列
            • 判断是否为平衡二叉树
            • 两数之和、三数之和

实现一下观察者模式
class Subject {
    constructor () {
        this.state = 0
        this.observe = []
    }
    getState () {
        return this.state
    }
    setState (val) {
        this.state = val
        this.observe.forEach((item) => {
            item.update()
        })
    }
    attach(ob) {
        this.observe.push(ob)
    }
}

class Observe {
    constructor (name, sub) {
        this.name = name
        this.sub = sub
        this.sub.attach(this)
    }
    update () {
        console.log(`${this.name} update, state: ${this.sub.getState()}`)
    }
}
实现一下工厂模式
// 工厂模式
class Product {
    constructor (name) {
        this.name = name
    }
    getName () {
        console.log(this.name)
    }
    init () {

        console.log('init')
    }
}

class Factory {
    create (name) {
        return new Product(name)
    }
}
实现一下单例模式
class LoginForm {
    constructor () {
        this.state = 'hide'
    }
    show () {
        if (this.state === 'show') {
            console.log('已经show')
        }
        this.state = 'show'
        console.log('登录框show成功')
    }
    hide () {
        if (this.state === 'hide') {
            console.log('已经hide')
        }
        this.state = 'hide'
        console.log('登录框hide成功')
    }
}

LoginForm.getInstance = (function () {
    let instance = null
    return function () {
        if (!instance) {
            instance = new LoginForm()
        }
        return instance
    }
})()
设计一个lazyMan,实现以下功能:

LazyMan(‘Tony’);
// Hi I am Tony

LazyMan(‘Tony’).sleep(10).eat(‘lunch’);
// Hi I am Tony
// 等待了10秒…
// I am eating lunch
LazyMan(‘Tony’).eat(‘lunch’).sleep(10).eat(‘dinner’);
// Hi I am Tony
// I am eating lunch
// 等待了10秒…
// I am eating diner
LazyMan(‘Tony’).eat(‘lunch’).eat(‘dinner’).sleepFirst(5).sleep(10).eat(‘junk food’);
// Hi I am Tony
// 等待了5秒…
// I am eating lunch
// I am eating dinner
// 等待了10秒…
// I am eating junk food

考察点:运用发布订阅者模式,js事件执行机制

function lazyMan(name) {
    // 任务清单队列
    this.taskList = []
    this.name = name
    console.log(`Hi I am ${name}`)
    setTimeout(() => this.next())
}
lazyMan.prototype = {
    // 订阅方法
    eat (food) {
        const fn = () => {
            console.log(`i am eating ${food}`);
            this.next()
        }
        this.taskList.push(fn)
        return this
    },
    sleepFirst (time) {
        const fn = () => {
            setTimeout(() => {
                console.log(`等待了${time}秒`);
                this.next()
            }, time*1000)
        }
        this.taskList.unshift(fn)
        return this
    },
    sleep (time) {
        const fn = () => {
            setTimeout(() => {
                console.log(`等待了${time}秒`);
                this.next()
            }, time*1000)
        }
        this.taskList.push(fn)
        return this
    },
    // 事件发布
    next () {
        const fn = this.taskList.shift()
        fn && fn()
    }
}
function LazyMan (name) {
    return new lazyMan(name)
}

LazyMan('Tony')
LazyMan('Tony').sleep(10).eat('lunch')
LazyMan('Tony').eat('lunch').sleep(10).eat('dinner')
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food')
实现一个简单的EventEmitter

就是事件的触发机制,发布订阅者的一个实现,和上题思路一样

function eventEmitter () {
	// 定义事件池
    this.eventpool = {}
    // 事件绑定
    this.on = function (event, callback) {
        this.eventpool[event] ? this.eventpool[event].push(callback) : this.eventpool[event]
    }
    // 事件分发
    this.emit = function (event, ...args) {
        this.eventpool[event] && this.eventpool[event].forEach((cb) => cb(...args))
    }
    // 事件解绑
    this.off (event) {
        if (this.eventpool[event]) {
            delete this.eventpool[event]
        }
    }
    // 事件只绑定执行一次
    this.once(event, callback) {
        this.on(event, (...args) => {
            callback(...args)
            this.off(event)
        })
    }
}

考察点:事件触发与事件监听器功能的封装

以上两个手撕题涉及的
发布 + 订阅
DOM 的事件机制就是发布订阅模式最常见的实现,这大概是前端最常用的编程模型了,监听某事件,当该事件发生时,监听该事件的监听函数被调用。
————————————————————————————————————
发布订阅模式,阮一峰在《Javascript 异步编程的 4 种方法》,中:

我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe
pattern),又称"观察者模式"(observer pattern)。

手撕实现原生js的一些方法(call、apply、bind、map、reduce、filter、splice、pop、push、unshift、shift)
  • 实现call方法
// call的实现(实现call)
Function.prototype.call = function (context) {
  context = context ? Object(context) : window
  context.fn = this
  let args = [...arguments].slice(1)
  let res = context.fn(...args)
  delete context.fn
  return res
}
  • 实现apply方法
// apply的实现
Function.prototype.apply = function (context, arr) {
  context = context ? Object(context) : window
  context.fn = this
  let res
  if (!arr) {
    res = context.fn()
  } else {
    res = context.fn(...arr)
  }
  delete context.fn
  return res
}
  • 实现bind方法
// 函数实现
function bind(fn, context) {
    let args = Array.prototype.slice.call(arguments, 2)
    return function () {
        return fn.apply(context, args.concat(Array.prototype.slice.call(arguments)))
    }
}
// 原型链修改
Function.prototype.bind = function (context) {
    let that = this
    let args = Array.prototype.slice.call(arguments, 1)
    return function () {
        return that.apply(context, args.concat(Array.prototype.slice.call(arguments)))
    }
}

// 更为完整的方法
Function.prototype.bind2 = function (context) {

    if (typeof this !== "function") {
      throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
    }

    var self = this;
    var args = Array.prototype.slice.call(arguments, 1);

    var fNOP = function () {};

    var fBound = function () {
        var bindArgs = Array.prototype.slice.call(arguments);
        return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
    }

    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
}
  • 实现map方法
// 不使用其他js函数实现map
Array.prototype.map = function (fn) {
    let arr = []
    for (let i = 0; i < this.length; i++) {
        arr. push(fn(this[i], i, this))
    }
    return arr
}
// 使用reduce实现map
Array.prototype.map = function (fn) {
    return this.reduce((arr, cur, index) => {
        arr.push(fn(cur, index, this))
        return arr
    }, [])
}
  • 实现reduce方法
// 实现reduce
Array.prototype.reduce = function (fn, initVal) {
    let res = initVal ? initVal : 0
    for (let i = initVal ? 1 : 0; i < this.length; i++) {
        res = fn(res, this[i], i, this)
    }
    return res
}
  • 实现filter方法
Array.prototype.filter = function (fn) {
    let arr = []
    for (let i = 0; i < this.length; i++) {
        if (fn(this[i], i, this)) {
            arr.push(this[i])
        }
    }
    return arr
}
  • 实现splice方法
// 实现原生splice
Array.prototype.splice = function (again, num) {
    let frontArr = []
    let afterArr = []
    let args = Array.prototype.slice.call(arguments, 2)
    for (let i = 0; i < again; i++) {
        frontArr[i] = this[i]
    }
    if (args.length) {
        for (let i = 0; i < args.length; i++) {
            frontArr[again + i] = this[i]
        }
    }
    for (let i = again + num, j = 0; i < this.length; i++, j++) {
        afterArr[j] = this[i]
    }

    let deleteArr = []
    for (let i = 0; i < num; i++) {
        deleteArr[i] = this[again + i]
    }
    let a = frontArr.concat(afterArr)
    this.length = a.length
    for (let i = 0; i < a.length; i++) {
        this[i] = a[i]
    }
    return deleteArr
}
  • 实现push方法
Array.prototype.push = function () {
    let args = arguments
    for (let i = 0; i < args.length; i++) {
        this[this.length] = args[i]
    }
    return this.length
}
  • 实现pop方法
Array.prototype.pop = function () {
    if(this.length === 0) return
    let val = this[this.length - 1]
    this.length -= 1
    return val
}
  • 实现unshift方法
Array.prototype.unshift = function () {
    let args = [...arguments]
    let len = args.length
    for (let i = this.length - 1; i >= 0; i--) {
        this[i + len] = this[i]
    }
    for (let i = 0; i < len; i++) {
        this[i] = args[i]
    }
    return this.length
}
  • 实现shift方法
Array.prototype.shift = function () {
    let removeVal = this[0]
    for (let i = 0; i < this.length; i++) {
        if (i !== this.length - 1) {
            this[i] = this[i + 1]
        }
    }
    this.length -= 1
    return removeVal
}
手撕实现一下前端路由

考察点:
前端路由的两种实现方式
1、location.hash+hashchange事件。
2、history.pushState()+popstate事件。

// 这里用hash的路由方法实现
function Router() {
	this.routes = {}
	this.curUrl = ''
	this.init()
}

Router.prototype.route = function (path, cb) {
	this.routes[path] = cb || function () {}
}

Router.prototype.refresh = function () {
	this.curUrl = location.hash.slice(1) || '/'
	this.routes[this.curUrl] && this.routes[this.curUrl]()
}

Router.prototype.init = function () {
	window.addEventListener('load', this.refresh.bind(this))
	window.addEventListener('hashchange', this.refresh.bind(this))
}


// 用个例子试用一下
var router = new Router()
router.route('/', function () {
  var body = document.getElementById('page-type')
  body.innerHTML = '首页耶耶耶'
})
router.route('/news', function () {
  var body = document.getElementById('page-type')
  body.innerHTML = '新闻耶耶耶'
})
简单实现一下实现事件委托(ul中很多个li标签)
window.onload = function () {
    let ul1 =  document.getElementById('ul1')
    let li = ul1.getElementsByTagName('li')
    // 方法一(挨个li标签加方法)---不推荐
    // for (let i = 0; i < li.length; i++) {
    //     li[i].addEventListener('click', function (e) {
    //         alert(li[i].innerHTML)
    //     })
    // }
    // 方法二:(事件委托)
    ul1.onclick = function (e) {
        var ev = e || window.event
        if (ev.target.nodeName.toLowerCase() === 'li') {
            alert(ev.target.innerHTML)
        } 
    }
}
用setTimeOut实现一下setInterval
function mySetInterval (cb, time) {
    const fn = () => {
        cb()
        setTimeout(() => {
            fn()
        }, time)
    }
    setTimeout(fn, time)
}

实现一下curry函数(柯里化函数)
// 函数柯里化
// 方式一:
// function curry (fn) {
//   var args = Array.prototype.slice.call(arguments, 1)
//   return function () {
//     return fn.apply(null, args.concat(Array.prototype.slice.call(arguments)))
//   }
// }
// 方式二:
let curry = (fn) =>
        judge = (...args) =>
            args.length >= fn.length ?
                fn(...args): (...arg) => judge(...args, ...arg)
// 例子
function add (a, b, c) {
  return a + b + c
}
var res = curry(add(1))
var res1 = res(2)
console.log(res1(3))
实现类似 add(1)(2) add(1, 2, 3)(10) add(1)(2)(3)(4)(5)的函数

考察点:
主要是要实现 既返回函数又返回计算结果
所以重写函数本身的toString()方法,在函数转换时它会自动调用。

先来简单了解下这两个方法:
Object.prototype.valueOf():
用 MDN 的话来说,valueOf() 方法返回指定对象的原始值。

JavaScript 调用 valueOf() 方法用来把对象转换成原始类型的值(数值、字符串和布尔值)。但是我们很少需要自己调用此函数,valueOf 方法一般都会被 JavaScript 自动调用。

Object.prototype.toString():
toString() 方法返回一个表示该对象的字符串。

每个对象都有一个 toString() 方法,当对象被表示为文本值时或者当以期望字符串的方式引用对象时,该方法被自动调用。

这里先记住,valueOf() 和 toString() 在特定的场合下会自行调用。

原始类型:
好,铺垫一下,先了解下 javascript 的几种原始类型,除去 Object 和 Symbol,有如下几种原始类型:

Number
String
Boolean
Undefined
Null

function sum() {
    let args = [].slice.call(arguments);
    let fn = function () {
        let fn_args = [].slice.call(arguments)
        return sum.apply(null, args.concat(fn_args))
    }
    fn.toString = function () {
        return args.reduce((a, b) => a + b)
    }
    return fn
}
实现一下es5的几种继承方法
// 1:原型链继承
function Cat1 () {
}
Cat1.prototype = new Animal()
// 2:构造函数继承
function Cat2 (name) {
    Animal.call(this)
    this.name = name || 'Cat'
}
// 3: 实例继承
function Cat3 (name) {
    let instance = new Animal()
    instance.name = name || 'Cat'
    return instance
}
// 4:拷贝继承
function Cat4 (name) {
    var animal = new Animal()
    for (let p in animal) {
        Cat4.prototype[p] = animal[p]
    }
    Cat4.prototype.name = name || 'Cat'
}
// 5: 组合继承(常用)
function Cat5 () {
    Animal.call(this)
    this.name = name || 'Cat'
}
Cat5.prototype = new Animal()
Cat5.prototype.constructor = Cat5
// 6:寄生组合继承
function Cat5 () {
    Animal.call(this)
    this.name = name || 'Cat'
}
(function () {
    var Super = function () {}
    Super.prototype = Animal.prototype
    Cat5.prototype = new Super()
})()
Cat5.prototype.constructor = Cat5
代码实现一下new方法过程

考察点:
(new的过程:
• 创建一个空对象,将它的引用赋给 this,继承函数的原型。
• 通过 this 将属性和方法添加至这个对象
• 最后返回 this 指向的新对象,也就是实例(如果没有手动返回其他的对象)

// new的过程
function parent (name, age) {
    this.name = name
    this.age = age
}
parent.prototype.getName = function () {
    console.log(this.name);
}

let newObj = function (par) {
    let args = Array.prototype.slice.call(arguments, 1)
    let child = Object.create(par.prototype)
    par.apply(child, args)
    return child
}

let children = newObj(parent, 'haha', 22)

children.getName()
console.log(children.hasOwnProperty('say'));

用函数实现new:

function creat () {
  // 获得父类构造函数。(也就是传入的arguments中的第一个参数)
  var Con = [].shift.call(arguments)
  // 创建一个空对象并将其链接到原型,使得可以访问到构造函数的原型属性
  var obj = Object.create(Con.prototype)
  // 绑定this实现继承,obj可以访问构造函数中的属性
  var result = Con.apply(obj, arguments)
  // 优先返回构造函数返回的对象
  return result instanceof Object ? result : obj
}
简单实现promise方法

考察点:
promise的原理及各种状态

function myPromise (fn) {
    let val = null, succcallbacks = [], failcallbacks = []
    let data = null, reason = null, status = 'pending'
    
    this.then = function (fulfilled, rejected) {
        if (status === 'pending') {
            succcallbacks.push(fulfilled)
            failcallbacks.push(rejected)
            return this
        } else if (status === 'resolve') {
            fulfilled(data)
        } else {
            rejected(reason)
        }
    }
    function resolve (val) {
        setTimeout(() => {
            data = val
            status = 'fulfilled'
            succcallbacks.forEach((callback) => {
                callback(val)
            })
        }, 0)
    }
    function reject (val) {
        setTimeout(() => {
            reason = val
            status = 'rejected'
            failcallbacks.forEach((callback) => {
                callback(val)
            })
        }, 0)
    }
    fn(resolve, reject)
}
// 测试
function test(num) {
    return new myPromise((resolve, reject) => {
        setTimeout(() => {
            resolve(num)
        }, 1000)
    })
}
实现一个promise失败重传函数(重传限定多少次)
// promise重传
function tryPromise (fn, count) {
  fn().then((result) => {
    return result
  }, (err) => {
    if (err) {
      if (count) {
        tryPromise(fn, count--)
      }
    }
  })
}
实现一个基于promise的ajax函数
// 使用promise封装ajax函数
var xmlhttp = new XMLHttpRequest()
function ajax (type, api, data) {
    return new Promise((resolve, reject) => {
        xmlhttp.onreadystatechange = () => {
            if (xmlhttp.readyState === 4) {
                if (xmlhttp.status === 200) {
                    resolve(xmlhttp.responseText)
                } else {
                    reject(xmlhttp.status)
                }
            }
        }
        xmlhttp.open(type, api, data)
        xmlhttp.send
    })
}
实现一个深拷贝函数
function deepCopy (obj) {
  if (!obj instanceof Object) {
    throw new Error('Not a Object')
  }
  var newObj = Array.isArray(obj) ? [] : {}
  for (let key in obj) {
    newObj[key] = obj[key] instanceof Object ? deepCopy(obj[key]) : obj[key]
  }
  return newObj
}
用函数实现一下instanceof的功能
function instance_of(L, R){ // 表示表达式左边R和右边L
  R = R.prototype // 取 R 的显示原型
  L = L.__proto__ // 取 L 的隐式原型
  while (true) {
    if (L === null) return false // Object.prototype.__proto__ === null
    if (R === L) return true // 这里重点:当 O 严格等于 L 时,返回 true(即实例在原型链上)
    L = L.__proto__
  }
}
实现防抖和节流函数
// 防抖
function debounce (fn) {
    let instance = null
    return function () {
        clearTimeout(instance)
        instance = setTimeout(fn, 2000)
    }
}
// 节流
function throttle (fn, wait, maxTime) {
    let instance = null
    let startTime = new Date()
    return function () {
        let endTime = new Date()
        let args = arguments
        let that = this
        clearTimeout(instance)
        if (endTime - startTime >= maxTime) {
            fn.apply(that, args)
            startTime = endTime
        } else {
            instance = setTimeout(fn, wait)
        }
        
    }
}
实现数组扁平化函数flatten
// 数组扁平化
let flatArr123 = [1, 3, [2, [5, 6]], [4], [[6, 7, [8]]]]
// 1:
flatArr123.flat(Infinity)
// 2:
function flatten2(arr) {
    return arr.reduce((result, item) => {
        return result.concat(Array.isArray(item) ? flatten2(item) : item)
    }, [])
}
// 3:
function flatten3(arr) {
    return arr.toString().split(',').map(x => +x)
}
// 4:
function flatten4(arr) {
    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr)
    }
    return arr
}
// 5:
function flatten5(arr) {
    var res = []
    arr.map(item => {
        if (Array.isArray(item)) {
            res = res.concat(flatten5(item))
        } else {
            res.push(item)
        }
    })
    return res
}
创建形如a.b.c.d嵌套对象
function buildObj(dataObjStr) {
    var array = dataObjStr.split(".")
    var result = {}
    var temp = result
    for (var i = 0; i < array.length; i++) {
        temp = temp[array[i]] = {}
    }
    return result
}
二叉树的前中后序(深度优先)及层序(广度优先)遍历

递归:

// 前序遍历
function preorderTraversal(root, arr = []) {
  if (root === null) return
  arr.push(root.val)
  preorderTraversal(root.left, arr)
  preorderTraversal(root.right, arr)
  return arr
}
// 中序遍历
function inorderTraversal(root, arr = []) {
  if (root === null) return
  inorderTraversal(root.left, arr)
  arr.push(root.val)
  inorderTraversal(root.right, arr)
  return arr
}
// 后序遍历
function postorderTraversal(root, arr = []) {
  if (root === null) return
  postorderTraversal(root.left, arr)
  postorderTraversal(root.right, arr)
  arr.push(root.val)
  return arr
}

非递归:

// 前序遍历
function preorderTraversal(root) {
  var stack = []
  var res = []
  var p = root
  if (root === null) return []
  while (p || stack.length) {
    while (p) {
      stack.push(p)
      res.push(p.val)
      p = p.left
    }
    p = stack.pop()
    p = p.right
  }
  return res
}
// 中序遍历
function inorderTraversal(root) {
  var stack = []
  var res = []
  var p = root
  if (root === null) return []
  while (p || stack.length) {
    while (p) {
      stack.push(p)
      p = p.left
    }
    p = stack.pop()
    res.push(p.val)
    p = p.right
  }
  return res
}
// 后序遍历
function postorderTraversal(root, arr = []) {
  var stack = []
  var res = []
  var p = root
  while (p || stack.length) {
    if (p) {
      stack.push(p)
      res.unshift(p.val)
      p = p.right
    } else {
      var node = stack.pop()
      p = node.left
    }
  }
  return res
}

层序遍历:

function isCompleteTree (tree) {
    let queue = []
    queue.push(tree)
    while (queue.length) {
        let p = queue.shift()
        if (p) {
            queue.push(p.left)
            queue.push(p.right)
        } else {
            while (queue.length) {
                if (queue.shift()) {
                    return false
                }
            }
        }
    }
    return true
}

建立完全二叉树测试:

// 这里用个简单的递归方法简历完全二叉树来测试
// 树的节点
function bitTreeNode(val) {
  this.val = val
  this.right = null
  this.left = null
}
// 递归建立
function creatTree(n) {
  if (n === 1) return new bitTreeNode(1)
  let root = new bitTreeNode(n)
  root.left = creatTree(n - 1)
  root.right = creatTree(n - 1)
  return root
}
var tree = creatTree(5)
console.log(tree)
实现以下lodash的_get()函数

知识点:
主要实现对象的嵌套调用情况,如 a.b.c.d.e,因为这么写很容易抛出异常。(正常写法得 a && a.b && a.b.c && a.b.c.d && a.b.c.d.e)

function _get (source, path, defaultValue = undefined) {
    const paths = path.replace(/\[(\d+)\]/g, '.$1').split('.')
    let res = source
    for (let val of paths) {
        res = Object(res)[val]
        if (res === undefined) {
            return defaultValue
        }
    }
    return res
}
实现将url中?后的参数转化为js对象
function urlToObject () {
    let url = window.location.href
    url = url.indexOf('?') === -1 ? 'https://www.nowcoder.com/search?type=post&order=time&query=前端&subType=0&page=2' : url.split('?')[1].join('')
    let r = new RegExp(/([^?&=]+)=([^?&=]+)/g)
    var obj = {}
    url.replace(r, function (res, $1, $2) {
        console.log(res, $1, $2)
        obj[$1] = $2
    })
    console.log(obj)
}
实现大数相加

知识点:

因为JavaScript的Number类型是遵循IEEE 754规范表示的,这就意味着JavaScript能精确表示的数字是有限的,JavaScript可以精确到个位的最大整数是9007199254740992,也就是2的53次方,超过这个范围就会精度丢失,造成JavaScript无法判断大小。

function maxSum (str1, str2) {
    if (typeof str1 !== 'string' || typeof str2 !== 'string') {
        throw new Error('传入参数不是字符串')
    }
    let arr1 = str1.split('').reverse()
    let arr2 = str2.split('').reverse()

    let Len = Math.max(arr1.length, arr2.length)
    let flag = 0
    let res = []

    for (let i = 0; i < Len; i++) {
        arr1[i] = Number(arr1[i]) || 0
        arr2[i] = Number(arr2[i]) || 0
        let sum = arr1[i] + arr2[i] + flag
        if (sum >= 10) {
            flag = 1
            sum = sum % 10
        } else {
            flag = 0
        }
        res.push(sum)
        if (i === Len - 1 && flag) {
            res.push(flag)
        }
    }
    return res.reverse().join('')
}
字符串的全排列
function permutation (str) {
    let res = [str[0]]
    for (let i = 1; i < str.length; i++) {
        res = fp(res, str[i])
    }
    return [...new Set(res)].sort()
}

function fp (arr, ch) {
    let tmp = []
    for (let i = 0; i <= arr[0].length; i++) {
        tmp = tmp.concat(arr.map((x) => x.slice(0, i) + ch + x.slice(i)))
    }
    return tmp
}
// console.log(permutation('abc'));
判断是否为平衡二叉树

知识点:
运用层序遍历的知识点来判断是否为平衡二叉树

function isCompleteTree (tree) {
    let queue = []
    queue.push(tree)
    while (queue.length) {
        let p = queue.shift()
        if (p) {
            queue.push(p.left)
            queue.push(p.right)
        } else {
            while (queue.length) {
                if (queue.shift()) {
                    return false
                }
            }
        }
    }
    return true
}
两数之和、三数之和

ps:具体题目百度(面试常考算法)

// 两数之和
function twoSum (arr, sum) {
    let flag = {}
    for (let i = 0; i < arr.length; i++) {
        if (!flag[arr[i]]) {
            flag[sum - arr[i]] = arr[i]
        } else {
            return [arr[i], flag[arr[i]]]
        }
    }
}
// 三数之和
function threeSum (arr, sum) {
    let res = []
    if (!arr.length) return res
    arr.sort((a, b) => a - b)
    for (let i = 0; i < arr.length - 1; i++) {
        if (arr[i] >= sum) return res
        if (arr[i] === arr[i + 1]) continue
        let L = i + 1
        let R = arr.length - 1
        while (L < R) {
            if (arr[i] + arr[L] + arr[R] === sum) {
                res.push([arr[i], arr[L], arr[R]])
                while (L < R && arr[L] === arr[L + 1]) L++
                while (L < R && arr[R] === arr[R - 1]) R--
                L++
                R--
            }
            else if (arr[i] + arr[L] + arr[R] < sum) L++
            else if (arr[i] + arr[L] + arr[R] > sum) R--
        }
    }
    return res
}

你可能感兴趣的:(前端基础)