前端经典算法题大全

整理了一些面试中常用的算法题,正在不断更新中,大家在面试和工作中有常遇到的算法题,,欢迎评论区共享

1 查找字符串中的最长公共前缀

示例: 输入: [“flower”,“flow”,“flight”]
输出: “fl”

function getMaxAndIndex( arr ){
  var firstStr = arr[0], result = ''
  if(!arr.length) return result
  for(let i=0;i<firstStr.length;i++){
      for(let j=1;j<arr.length;j++){
        if(firstStr[i]!=arr[j][i]){
          return result
        }
        
      }
      result += firstStr[i]
  }
  return result
}

2 深度拷贝、浅度拷贝

  1. 浅拷贝
    a. 对象的合并 Object.assign(),第一个参数必须是个空对象

    var obj1 = {a: 1, b: 2};
    var obj2 = Object.assign({}, obj1);
    

    前端经典算法题大全_第1张图片
    b. 对象的解构赋值

    var obj1 = {a: 1, b: 2};
    var obj2 = {...obj1};
    
  2. 深度拷贝
    a. 数组或对象深拷贝(利用循环和递归)

    function deepClone(obj) {
      let objClone = Array.isArray(obj) ? [] : {};
      if (obj && typeof obj === 'object') {
        for (key in obj) {
          if (obj.hasOwnProperty(key)) {
            if (obj[key] && typeof obj[key] === 'object') {
              objClone[key] = deepClone(obj[key])
            } else {
              objClone[key] = obj[key]
            }
          }
        }
      }
      return objClone;
    }
    let a = [20,8,6,19],
    b = deepClone(a);
    a[1] = 2;
    console.log(a, b)
    

    b. 利用jQuery的$.extend方法

    //第一个参数不传(false是不能够显示的写出来的)默认为false,是浅拷贝。传true为深拷贝。
    $.extend(true,object1, object2)
    
    //newObject 即为深拷贝出来的对象
    var newObject = $.extend(true , {} , object);
    

    c. JSON.parse( JSON.stringify())

    //通过js的内置对象JSON来进行数组对象的深拷贝
    function deepClone(obj) {
      var _obj = JSON.stringify(obj),
        objClone = JSON.parse(_obj);
      return objClone;
    }
    

3 手写EventEmiter

class EventEmitter {
      constructor() {
        this.events = {}
      }
      on(eventName, fn) {
        if (!this.events[eventName]) {
          this.events[eventName] = []
        }
        this.events[eventName].push(fn)
      }
      emit(eventName, ...rest) {
        this.events[eventName] && this.events[eventName].forEach(f => f.apply(this, rest))
      }
      // remove(eventName, fn) {
      //   if (this.events[eventName]) {
      //     this.events[eventName].filter(f => f !== fn)
      //   }
      // }
      // once(eventName, callback) {
      //   const fn = () => {
      //     callback();
      //     this.remove(eventName, fn)
      //   }
      //   this.on(eventName, fn)
      // }
    }
    const event = new EventEmitter()
    const handle = (...pyload) => console.log(pyload)
    event.on('click', handle)
    // event.emit('click', 100, 200, 300, 100)
    // event.remove('click', handle)
    event.once('dbclick', function () {
      console.log('click')
    })
    event.emit('dbclick', 100);

3 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值,返回 [-1, -1]。

示例: 输入: nums = [5,7,7,8,8,10], target = 8, 输出:[3,4]
输入: nums = [5,7,7,8,8,10], target = 6, 输出:[-1,-1]
思路:1. 开始位置 indexOf
2. 结束位置:翻转数组,求arr.length-(indexOf+1)

function pop(arr, target){
     const tmp = [];
     tmp[0] = arr.indexOf(target);
     tmp[1] = arr.reverse().indexOf(target);
     if(tmp[1] !== -1){
      tmp[1] = arr.length - tmp[1]-1;
     }
     console.log(tmp);
     return tmp;
 }
  const nums = [5,7,7,8,8,10];
  const target = 8;
  pop(nums, target)

4 js实现promise.all

function PromiseAll(promise){
      return new Promise((resolve, reject) => {
        let index=0;
        let result = [];
        if(promise.length === 0) resolve(result)
        for(let i = 0; i<promise.length; i++){
          Promise.resolve(promise[i]).then(data => {
            result[i] = data;
            if(++index === promise.length){
              resolve(result)
            } 
          }, err => {
            reject(err)
          })
        }
      })
  }

   let p1 = new Promise((resolve) => {
     resolve('成功了')
   })
   let p2 = new Promise((resolve) => {
     resolve('success')
   })
   PromiseAll([p1, p2]).then((result) => {
     console.log(result) //['成功了', 'success']
   }).catch((error) => {
     console.log(error)
   })
输出结果:(2) ["成功了", "success"]

5.判断一个单词是否是回文?

回文是指把相同的词汇或句子,在下文中调换位置或颠倒过来,产生首尾回环的情趣,叫做回文,也叫回环。比如 mamam redivider .

很多人拿到这样的题目非常容易想到用for 将字符串颠倒字母顺序然后匹配就行了。其实重要的考察的就是对于reverse的实现。其实我们可以利用现成的函数,将字符串转换成数组,这个思路很重要,我们可以拥有更多的自由度去进行字符串的一些操作。

function checkPalindrom(str) {  
    return str == str.split('').reverse().join('');
}

6. 去掉一组整型数组重复的值

比如输入: [1,13,24,11,11,14,1,2]
输出: [1,13,24,11,14,2]
需要去掉重复的11 和 1 这两个元素。

let unique = function(arr) {  
  let hashTable = {};
  let data = [];
  for(let i=0,l=arr.length;i

7.统计一个字符串出现最多的字母

输入 : afjghdfraaaasdenas

输出 : a
前面出现过去重的算法,这里需要是统计重复次数。

function findMaxDuplicateChar(str) {  
  if(str.length == 1) {
    return str;
  }
  let charObj = {};
  for(let i=0;i= maxValue) {
      maxChar = k;
      maxValue = charObj[k];
    }
  }
  return maxChar;

}

另一种写法:

function getMaxAndIndex( arr ){
  if(!arr.length) return;
  if(arr.length==1) return 1;
  var h={}, maxnum=0,maxel='';
  for(var i=0;imaxnum){
      maxnum=h[a]
      maxel=a
    }
  }
  return '出现次数最多的元素为:'+maxel+'出现次数:'+maxnum
}

8.排序算法

冒泡排序
思路:它重复地走访要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们位置交换过来.

function bubbleSort(arr) {  
    for(let i = 0,l=arr.length;i<l-1;i++) {
        for(let j=0;j<arr.length-1-i;j++) { 
          if(arr[j]>arr[j+1]) {
              [arr[j],arr[j+1]] = [arr[j+1], arr[j]]
            }
        }
    }
    return arr;
}

快速排序
思路:选择一个元素作为基数(通常是第一个元素),把比基数小的元素放到它左边,比基数大的元素放到它右边(相当于二分),再不断递归基数左右两边的序列。

function quickSort(arr){
    if(arr.length<=1)return arr;
    let leftArr=[],rightArr=[];
    for(let i=1;i< arr.length; i++){
      if(arr[i]<arr[0]){
        leftArr.push(arr[i])
      } else {
        rightArr.push(arr[i])
      }
    }
    return [].concat(quickSort(leftArr),arr[0],quickSort(rightArr))
 }

选择排序
思路:和冒泡排序相似,区别在于选择排序是将每一个元素和它后面的元素进行比较和交换。

function selectSort(arr){
      for(let i=0;i< arr.length; i++){
        for(let j=i+1;j< arr.length; j++){
          if(arr[i]>arr[j])
          [arr[i],arr[j]] = [arr[j], arr[i]]
        }
      }
      return arr;
}

插入排序
思路:依然要通过两层循环,外循环便利每个数组项,内循环从外循环的数组项(i)开始往前遍历,如果当前数组项比前一个小,则与前一个调换位置,这样一直循环重复,数组就逐渐归位了;

function insertSort(arr){
      for(let i=1;i< arr.length; i++){
        for(j=i;j>0;j--){
           if(arr[j]<arr[j-1]){
             	[arr[j], arr[j-1]]= [arr[j-1], arr[j]]
           }
         }
      }
      return arr
}

希尔排序
思路:希尔排序将插入排序作为它的子进程,它的核心是一个叫步长(gap)的概念,这个步长可以理解为一个类似疏密程度的概念。它共有3层循环,外层是以步长为循环,一般取数组的一半,循环一次再除一半,中层和里层就是插入排序的操作,不过不是跟前一项比,是跟当前索引减去步长后的那一项比。说到这里就可以理解为什么我说步长是类似疏密程度的概念,当步长不断除于2,变得越来越小,直到零停止循环,这个过程中,插入排序的比较项间越来越近,逐渐数组被排列出来。

function shellSort(arr){
      let gap = Math.floor(arr.length/2);
      while(gap>0){
        for(let i=gap;i< arr.length; i++){
          for(let j=i;j>0;j-=gap ){
            if(arr[j]<arr[j-gap])
            [arr[j], arr[j-gap]] = [arr[j-gap],arr[j]]
          }
        }
        gap = Math.floor(gap/2)
      }
      return arr
 }

桶排序
思路:取 n 个桶,根据数组的最大值和最小值确认每个桶存放的数的区间,将数组元素插入到相应的桶里,最后再合并各个桶。

function bucketSort(arr){
      let data = Array.from({length:10}).fill(0);
      let newArr = [];
      arr.forEach(el=>data[el]++)
      for(let i=0;i< data.length; i++){
        for(let j=0;j< data[i]; j++){
          newArr.push(i)
        }
      }
      return newArr;  
 }

二叉树查找

 let tree = { // 树结构 
      id: 1,
      left: { id: 2,  left: { id: 4}, right: { id: 5 }},
      right: { id: 3, left: { id: 6}, right: { id: 7 }}
    }
    // 1.根左右 前序递归 1245367
    function rootLR(tree) {
      console.log("===", tree.id);
      if (tree.left) rootLR(tree.left);
      if (tree.right) rootLR(tree.right);
    }
    // 2.左跟右 中序遍历 4251637
    function LRootR(tree) {
      if (tree.left) LRootR(tree.left);
      console.log("===", tree.id);
      if (tree.right) LRootR(tree.right);
    }
    // 3.左右跟 后序遍历 4526731
    function LRRoot(tree) {
      if (tree.left) LRRoot(tree.left);
      if (tree.right) LRRoot(tree.right);
      console.log("===", tree.id);
    }
    // 4.根左右 前序非递归 1245367
    function w_rootR(tree) {
      let res = [],
        arr = [tree]
      while (arr.length) {
        let tmp = arr.pop();
        res.push(tmp.id);
        if (tmp.right) arr.push(tmp.right)
        if (tmp.left) arr.push(tmp.left)
      }
      console.log(res);
      return res
    }
    //5 左根右 中序非递归 4251637
    function w_LRootR(tree) {
      let arr = [],
        res = [];
      while (true) {
        while (tree) {
          arr.push(tree);
          tree = tree.left;
        }
        if (!arr.length) break;
        let tmp = arr.pop();
        res.push(tmp.id);
        tree = tmp.right;
      }
      return res;
    }
    // 6左右跟 后序非递归遍历 4526731
    function w_LRRoot(tree) {
      let arr = [tree],
        res = [];
      while (arr.length) {
        let tmp = arr.pop();
        res.push(tmp.id);
        if (tmp.left) arr.push(tmp.left)
        if (tmp.right) arr.push(tmp.right)
      }
      return res.reverse();
    }

9.不借助临时变量,进行两个整数的交换

输入 a = 2, b = 4 输出 a = 4, b =2
主要是利用 + - 去进行运算,类似 a = a + ( b - a) 实际上等同于最后 的 a = b;

function swap(a , b) {  
  b = b - a;
  a = a + b;
  b = a - b;
  return [a,b];
}

使用canvas 绘制一个有限度的斐波那契数列的曲线?

前端经典算法题大全_第2张图片
斐波那契数列,又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列主要考察递归的调用。我们一般都知道定义

fibo[i] = fibo[i-1]+fibo[i-2];

生成斐波那契数组的方法

function getFibonacci(n) {  
  var fibarr = [];
  var i = 0;
  while(i

剩余的工作就是利用canvas arc方法进行曲线绘制了

10.出下列正数组的最大差值比如:

输入 [10,5,11,7,8,9]

输出 6

 function getMaxProfit(arr){
    var max=arr[0];
    var min=arr[0];
    var res=0;
    for (var i = 1; i < arr.length; i++) {
        if(arr[i]>max){
            max=arr[i];
        }
        if(arr[i]

11 随机生成指定长度的字符串

比如给定 长度 8 输出 4ldkfg9j

function getMaxStr(n) {
    let str = 'abcdefghijklmnopqrstuvwxyz9876543210';
    var tmp=''
    for(let i=0;i

12.实现类似getElementsByClassName 的功能

自己实现一个函数,查找某个DOM节点下面的包含某个class的所有DOM节点?不允许使用原生提供的 getElementsByClassName querySelectorAll 等原生提供DOM查找函数。





原生JS实现getElementsByClassName()




	
  • 0
  • 1
  • 2
  • 3
  • 4
  • 5

13.获取指定范围内的随机数

当我们需要获取指定范围(min,max)内的整数的时候,下面的代码非常适合。

function getRadomNum(min,max){
return Math.floor(Math.random() * (max - min + 1)) + min;
}

14.随机获取数组中的元素

function getRadomFromArr(arr){
   return arr[Math.floor(Math.random()*arr.length)];
}

15 生成从0到指定值的数字数组

 var arr=[]
 for(var i=1;i<=100;i++){
   arr.push(i)

 }
   console.log(arr)

16 打乱数字数组的顺序

var arr = [1,2,3,4,5,6,7,'a','dsfs',8,9,'v'];
arr.sort(function(){return Math.random()-0.5});

17 对象转换为数组

//注意对象必须是以下格式的才可以通过此方式转化为数组
//获取的DOM集合,以及函数的arguments也可以通过此方式转化为数组
var obj={
0:'qian',
1:'long',
2:'chu',
3:'tian',
length:4
}
var _slice=[].slice;
var objArr=_slice.call(obj);

18. 验证是否为数组

function isArray(obj){
  return Object.prototype.toString.call(obj) === '[object Array]' ;
}

19 获取数组中最大或者最小值

该方法适合一维或者多维数组求最大最小值的情况

function maxAndMin(arr){
	return {
	    max:Math.max.apply(null,arr.join(',').split(',')),
	    min:Math.min.apply(null,arr.join(',').split(','))
	}
}

20 清空数组

//方式一 通过将长度设置为0
var arr=[1,2,3,4,5];
arr.length=0;
 
//方式二 通过splice方法
var arr=[1,2,3,4,5];
arr.splice(0,arr.length);
 
//方式三 通过将空数组 [] 赋值给数组(严格意义来说这只是将ary重新赋值为空数组,之前的数组如果没有引用在指向它将等待垃圾回收。)
var arr=[1,2,3,4,5];
arr=[];

21保留指定小数位

var num =4.345678;
num = num.toFixed(4); // 4.3457 第四位小数位以四舍五入计算

22 找出数组中出现次数最的元素,并给出其出现过的位置

function getMaxAndIndex( arr ){
  var obj = {};
  arr.forEach(function(item,index){
  if(!obj[item]){
      obj[item]= {indexs: [index]}
  }else{
      obj[item]['indexs'].push(index);
  }
  });
  var num=0;//记录出现次数最大值
  var str='';//记录出现次数最多的字符
  var reArr;//返回最大值的位置数组
  for(var attr in obj){
    var temp=obj[attr]['indexs'];
    if(temp.length>num){
        num=temp.length;
        str=attr;
        reArr=temp;
    }
  }
  return {
    maxStr:str,
    indexs:reArr
  }
}

##手写 call / apply / bind

// 手写bind
    Function.prototype.bind = function (context, ...bindments) {
      context = context || window;
      const func = this;
      return function F(...callments) {
        let args = bindments.concat(callments);
        if (this instanceof F) return new func(...args)
        return func.call(context, ...args)
      }
    }
    // call
    Function.prototype.call = function (context, ...callargs) {
      context = context || window;
      context.func = this;
      if (typeof context.func !== 'function') {
        throw new TypeError("必须是函数调用call")
      }
      let res = context.func(...callargs);
      delete context.func;
      return res;
    }
    // apply
    Function.prototype.apply = function (context, arr) {
      context = context || window;
      context.func = this;
      if (typeof context.func !== 'function') {
        throw new TypeError("必须是函数调用apply")
      }
      let res = context.func(...arr)
      delete context.func;
      return res;
    }

你可能感兴趣的:(js,面试)