我知道的前端算法

前端算法大致可以分为几类

  • 排序
  • 堆栈,队列,链表
  • 递归,动态规划,贪心算法 二分查找

    以上内容面试官一般都会结合数组,字符串来考察你对以上知识的掌握程度

排序神图

这些排序里边主要掌握四个就可以:冒泡、选择、插入、快速

冒泡排序算法

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

图文:冒泡排序

选择排序

function selectionSort(arr) {
    var len = arr.length;
    var minIndex, temp;
    for (var i = 0; i < len - 1; i++) {
        minIndex = i;
        for (var j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {     // 寻找最小的数
                minIndex = j;                 // 将最小数的索引保存
            }
        }
        temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
    return arr;
}

图文选择排序

插入排序

function insertionSort(arr) {
    var len = arr.length;
    var preIndex, current;
    for (var i = 1; i < len; i++) {
        preIndex = i - 1;
        current = arr[i];
        while(preIndex >= 0 && arr[preIndex] > current) {
            arr[preIndex+1] = arr[preIndex];
            preIndex--;
        }
        arr[preIndex+1] = current;
    }
    return arr;
}

图文:插入排序

快速排序

function qSort(arr) {
  if (arr.length == 0) {
      return [];
  }
  var left = [];
  var right = [];
  var pivot = arr[0];
  for (var i = 1; i < arr.length; i++) { // 注意这里的起始值,因为有一个作为flag了
      if (arr[i] < pivot) {
          left.push(arr[i]);
      } else {
          right.push(arr[i]);
      }
  }
  return qSort(left).concat(pivot, qSort(right));
}

图文:快速排序

链表

单向链表的添加:

function insert(newElement, item){
    var newNode = new Node(newElement);  // 新建一个节点
    var current = this.find(item);   //找到item的位置(current在js中是引用,类似于C语言指针)
    newNode.next = current.next;  //将新节点的后继指向item的后继
    current.next = newNode;  // 修改item节点的后继指向新节点
}

单向链表的删除

// 首先要找到删除元素的上一个节点
function findPrevious (item) {
    var currentNode = this.head;
    while (!(currentNode.next === null) && (currentNode.element !== item)) {
    return currentNode;
    }
}

function remove (item) {
    var prevNode = this.findPrevious(item);
    var currentNode = this.find(item); // 查找到当前要删除的节点
    if (!(prevNode.next === null)) {
        prevNode.next = prevNode.next.next; //待删除节点的前驱的后继 指向 原本待删除节点的后继
        currentNode.next = null; // 释放节点,防止内存泄漏
    }
}

双向列表的添加:

// 插入节点,注意插入的链指向
function insert(newElement, item){
    var newNode = new Node(newElement);  // 新建一个节点
    var current = this.find(item);   //找到item的位置
    newNode.next = current.next;  //将新节点的后继指向item的后继
        newNode.previous = current;
        current.next = newNode;
        if (newNode.next !== null) {
                newNode.next.previous = newNode; // 将item原本的位置的前驱指向新节点
        }
}

双向链表的删除:

function remove (item) {
    var currentNode = this.find(item);
    if (!(currentNode === null)) {
        currentNode.previous.next = currentNode.next; // 删除节点的前驱的后继,指向删除节点的后继
        currentNode.next.previous = currentNode.previous; //删除节点的后继的前驱,指向删除节点的前驱
        currentNode.next = null; // 释放节点,防止内存泄漏
        currentNode.previous = null;
    } else {
        currentNode.previous.next = null; // 尾节点的前驱的后继指向null
        currentNode.previous = null; // 释放尾节点
    }
}

斐波那契数列

//递归方案
function gostair(stair){
  if(stair < 2){
    return stair;
}
return gostair(stair-1) + gostair(stair -2)
}

//动态规划方案
function gostair(n){
    let arr =  [];
    for(let i =  0;  i<=n;  i++){
      arr[i]  =  0;
    }
    if(n ==  1||n==2){
         return 1;
    }else{
        arr[1]  =  1;
        arr[2]  =  2;
        for(let j =  3;j<=n;j++){
        arr[j]  =  arr[j-1]+arr[j-2];
    }
    }
    return arr[n-1];
}
//迭代方案(指针移动方案)
function  gostair(n){
    let  first =  1; 
    let  last =  1;
    let result  =  1;
    for(let i=2;i<n;i++){
        result =  first +last;
        first =  last;
        last =  result;
    }
return result;
}

数组拍平

1.arr.join(',').split(',');
2.
function flat(arr) {
var newarr = [];
return function flatten(arr) {
    for(let i =  0;i<arr.length;i++){
        if (Object.prototype.toString.call(arr[i]) === "[object Array]") {
            newarr.concat(flatten(arr[i]))
        } else {
    newarr.push(arr[i]);
        }
    }(arr)
    return newarr
}

}

二分查找(前提是排序完成,最快的查找方法)

function find (arr, item) {
    var low = 0;  //设定下标
    var high = arr.length - 1; // 设定上标
    while (high >= low) {
        var mid = Math.floor((low + high) / 2);   //二分查找的关键
        if (arr[mid] === item) {
            return mid;
        }else if (arr[mid] > item) {
             high = mid;
        } else if (arr[mid] < item){
            low = mid;
        } else {
            return mid;
        }
    }
    return  -1;
}

浅拷贝

function extendCopy(p) {
  var c = {};
  for (var i in p) { 
    c[i] = p[i];
  }
  return c;
}

深拷贝:循环调用浅拷贝

function deepCopy(p, c) {
    var c = c || {};
    for (var i in p) {
      if (typeof p[i] === 'object') {
        c[i] = (p[i].constructor === Array) ? [] : {};
        deepCopy(p[i], c[i]);
      } else {
         c[i] = p[i];
      }
    }
    return c;
  }

用代码实现二叉搜索树,写出相关方法查找最小值。
(左子树上所有结点的值均小于它的根结点的值;右子树上所有结点的值均大于它的根结点的值;
)

//定义节点
    function Node(data, left, right) {
        this.data = data;
        this.left = left;
        this.right = right;
    }

// 插入值
function insert (data) {
    var n = new Node(data, null, null); // 定义一个新节点
    if(this.root === null) {
        this.root = n;
    } else {
        var current = this.root;
        var parent;
        while (true) {
            parent = current;
            if (data < current.data) {
                current = current.left; // 比当前值小就放左边
                if (current === null) {
                    parent.left = n;
                    break;
                }
            } else {    // 比当前值大就放右边
                current = current.right;
                if(current === null){
                    parent.right = n;
                    break;
                }
            }
        }
    }
}

// 找最小值
function getSmallest (root) {
    // 一直往左子树上去找,找到没有左节点即找到了最小值
    var current = this.root || root;
    while (!(current.left === null)) {
        current = current.left;
    }
    return current;
}

函数防抖,函数节流
防抖:debounce 操作结束后等待wait才执行一次。实例:keypress查询,防止点击提交按钮时的多次点击。

function debounce(fun,wait){
    var timeout  =  null;
    return function(){
    if(timeout !=  null) clearTimeout(timeout);
        timeout  =  setTimeout(fun,wait);
    }
}

如果immediate参数如果为true,则debounce函数会在调用时立刻执行一次function,等待时间内只执行一次

function debounce(fun,wait,immediate){
    var timeout;
    return function(){
        var context =  this,args =  arguments;
        var later =  function(){
        timeout =  null;
        if(!immediate) fun.apply(context,args);//immediate为false执行,操作结束后等待wait才执行一次
        };
        var callNow =  immediate &&   !timeout;
        clearTimeout(timeout);
        timeout =  setTimeout(later,wait);
        if(callNow)  fun.apply(context,args);//immediate为true执行,操作开始立即执行,

    }
}

节流:throttle 每隔多长时间执行一次函数,与防抖相比,节流函数多了一个 mustRun 属性,代表 mustRun 毫秒内,必然会触发一次 handler 实例:滑动加载图片懒加载

function  throttle(fun,wait,mustRun){
  var timeout,startTime =  new Date();
  return function(){
    var context =  this,args =  arguments,curTime =  new Date();
    clearTimeout(timeout);
    if(curTime -  startTime >=  mustRun){
        fun.apply(context,args);//mustRun内必触发一次
        startTime =  curTime;
    }else{
        timeout =  setTimeout(fun,wait);//每个wait触发一次
    }
  }

无限分类递归树

function toTree(data) {
   // 删除 所有 children,以防止多次调用
    data.forEach(function (item) {
        delete item.children;
    });

    // 将数据存储为 以 id 为 KEY 的 map 索引数据列
    var map = {};
    data.forEach(function (item) {
        map[item.id] = item;
    });
    var val = [];
    data.forEach(function (item) {

        // 以当前遍历项,的pid,去map对象中找到索引的id
        var parent = map[item.pid];

        // 好绕啊,如果找到索引,那么说明此项不在顶级当中,那么需要把此项添加到,他对应的父级中
        if (parent) {

            (parent.children || ( parent.children = [] )).push(item);

        } else {
            //如果没有在map中找到对应的索引ID,那么直接把 当前的item添加到 val结果集中,作为顶级
            val.push(item);
        }
    });

    return val;
}

获取两个时间中的所有年月

function getYearAndMonth(start, end) {
    var result = [];
    var starts = start.split('-');
    var ends = end.split('-');
    var staYear = parseInt(starts[0]);
    var staMon = parseInt(starts[1]);
    var endYear = parseInt(ends[0]);
    var endMon = parseInt(ends[1]);
    while (staYear <= endYear) {
        if (staYear === endYear) {
            while (staMon < endMon) {
                staMon++;
                result.push({year: staYear, month: staMon});
            }
            staYear++;
        } else {
            staMon++;
            if (staMon > 12) {
                staMon = 1;
                staYear++;
            }
            result.push({year: staYear, month: staMon});
        }
    }

    return result;
}
已标记关键词 清除标记
程序=数据结构+算法算法是真的不重要吗?单页应用 MVVM 框架、数据可视化、地图、游戏,都要用到算法,所以说我们前端工程师只是「身处其中而不自知」而已。 本场 Chat 作者为您分享亲身经历的跟算法相关的四个故事: 1. 第一份工作是做多级联动选择器 2. 终于写了个纯真 IP 库却被鄙视 3. 我常来面试别人的题目:EventBus 实现 4. 面挂经验:LRU Cache 实现 适合人群: 1. 如果你想听老司机讲故事,请购买此 Chat 2. 如果你想了解「自己用到却不自知」算法,请购买此 Chat 3. 如果你想从中得到一些算法或者数据结构的启蒙,请购买此 Chat **实录提要:** - 学习了算法,也刷了题,面试的时候还是不会做算法题目? - 前端工程师具体需要了解哪些算法? - 事件的触发和监听的具体通信过程是怎么实现的? - 在工作中遇到的场景和对应的算法有哪些? - 在大公司做前端,与在小公司做前端会有哪些不同? - 对刷 leetcode 有什么建议? - 前端加载速度慢,有什么方法或工具可以改进?这种情况和算法的关系大吗? - 前端在 PC 端和移动端的主要差异在哪里? - 想学习算法却不知从何入手,怎么看? - 级联下拉列表如果添加深度,会不会更好? - 前端埋点有哪些? - 对 ssr 和同构,有什么看法吗?同构是否有必要? *当前内容版权归码字科技所有并授权显示,盗版必究。[阅读原文](http://gitbook.cn/gitchat/activity/59e9e3eec6fa832bed6a5951)*
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页