前端知识点之算法

335 阅读4分钟

1.扁平数据结构转树

let arr = [
    {id: 1, name: '部门1', pid: 0},
    {id: 2, name: '部门2', pid: 1},
    {id: 3, name: '部门3', pid: 1},
    {id: 4, name: '部门4', pid: 3},
    {id: 5, name: '部门5', pid: 4},
]
// 输出结果:
[
    {
        "id": 1,
        "name": "部门1",
        "pid": 0,
        "children": [
            {
                "id": 2,
                "name": "部门2",
                "pid": 1,
                "children": []
            },
            {
                "id": 3,
                "name": "部门3",
                "pid": 1,
                "children": [
                    // 结果 ,,,
                ]
            }
        ]
    }
]

// 递归的方法
function arrayTotree(arr, result, pid) {
    arr.forEach((item) => {
        if (item.pid === pid) {
            const children = { ...item, children: [] };
            result.push(children);
            arrayTotree(arr, children.children, item.id);
        }
    });
};

const result = [];
arrayTotree(arr, result, 0);
console.log(result);
// 非递归的方法 借助对象的 Map
function arrayTotree(arr) {
    const result = [];
    const map = {};

    for (const item of arr) {
        const id = item.id;
        const pid = item.pid;
        if (!map[id]) {
            map[id] = { ...item, children: [] };
        }

        if (pid === 0) {
            result.push(map[id]);
        } else {
            // 容错处理
            if (!map[pid]) {
                map[pid] = { children: [] };
            }
            map[pid].children.push(map[id]);
        }
    }
    return result;
};
console.log(arrayTotree(arr));

2.爬楼梯

// 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
// 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
// 动态规划
var climbStairs = function(n) {
    if (n <= 2) return n;
    const arr = new Array(n).fill(0);
    arr[0] = 1;
    arr[1] = 2;
    for (let i = 2; i < n; i++) {
        arr[i] = arr[i - 1] + arr[i - 2];
    }
    return arr[n - 1]
};
console.log(climbStairs(6));

3.Promise的简单使用(间隔n秒后输出)

function sleep(n) {
    return new Promise((res) => {
        setTimeout(() => {
            res("hi~");
        }, n)
    });
};
async function promiseTest() {
    const time = Date.now();
    const res = await sleep(3000);
    console.log(res + "--------------->" + (Date.now() - time));
};

promiseTest();

4.函数被调用无数次,只执行n次

function fn (n) {
    let count = 0;
    return function () {
        if (count < n) {
            console.log("[run fn~]");
        }
        count++;
    }
}

const _fn = fn(2);
_fn();
_fn();
_fn();
_fn();

5.手写filter函数

Array.prototype._filter = function (callback, _this) {
    const res = [];
    for (let i = 0; i < this.length; i++) {
        if (callback.call(_this, this[i], i, this)) {
            res.push(this[i]);
        }
    }
    return res;
};

const arr = [1, 2, 3, 4, 5];
const _arr = arr._filter((value) => value > 3);
console.log(_arr);

6.求数组的最值和第二大的值

// 找数组中的最大值和第二大的值
// 时间复杂度控制在o(n)
// 不使用 Math.max() Array.sort()
function findArr (arr) {
    const len = arr.length;
    if (len === 0) return [];
    let max = -Infinity, _max = -Infinity;
    for (let i = 0; i < len; i++) {
        if (i === 0) {
            max = arr[0];
            _max = arr[0];
        } else {
            if (arr[i] > max) {
                _max = max;
                max = arr[i];
            }
            if (arr[i] < max && arr[i] > _max) {
                _max = arr[i];
            }
        }
    }
    return [max, _max];
}
console.log(findArr([3, 1, 2, 4, 5, 7, 10, 9]));

    console.log(findArr([3, 1, 2, 4, 5, 7, 10, 9]))

7.数组去重的5种方法

let arr = [1, 1, 1, 2, 11, 11, 12, 2, 2, 3, 4, 5];
// 1.通过 set
const resArr1 = [...new Set(arr)];
console.log(resArr1);
// 2.通过 forEach includes
const resArr2 = [];
arr.forEach(value => {
    if (!resArr2.includes(value)) {
    	resArr2.push(value);
    }
});
console.log(resArr2);
// 3.通过 forEach indexOf
const resArr3 = [];
arr.forEach(value => {
    if (resArr3.indexOf(value) === -1) {
    	resArr3.push(value);
    }
});
console.log(resArr3);
// 4.通过 filter
const resArr4 = arr.filter((value, index, array) => {
	return array.indexOf(value) === index;
});
console.log(resArr4);
// 5.通过 reduce
const resArr5 = arr.reduce((ans, value) => {
	return ans.includes(value) ? ans : [...ans, value];
}, []);
console.log(resArr5);

8.防抖和节流

防抖简单的来说就是在一系列连续触发的事件中,只触发最后一次事件的响应,适用于input等输入框的优化。

const fangdou = function(fn, delay) {
    let timer = null;
    return function() {
        clearTimeout(timer);
        timer = setTimeout(() => {
            fn.apply(this, arguments);
            clearTimeout(timer);
        }, delay);
    }
};

节流就是在一系列连续触发的事件中,只触发第一次事件的响应,适用于按钮的点击优化。

const jieliu = function(fn, delay) {
    let timer = null;
    return function() {
        if (!timer) {
            timer = setTimeout(() => {
                fn.apply(this, arguments);
                timer = null;
                clearTimeout(timer);
            }, delay);
        }
    }
};

9.手写call,apply,bind

call,apply,bind的作用是改变所调用函数的this指向,不同之处是call传入的函数是一个一个传入的,apply传入的是一个函数参数的数组,而bind会返回一个函数,参数也是一个一个传入的。

实现的思路也很简单,在所传入的对象中新添加一个属性,这个属性指向所调用的函数,使用这个传入的对象去调用函数,即可隐式的改变this的指向(this指向所调用的对象)。但是在bind函数中需要注意的是返回的函数可能使用new操作符调用,因为new的有的优先级比call,apply,bind高,那么此时应使用new初始化函数。此外,所返回的函数也可以传入参数。

call
Function.prototype.myCall = function(context, ...args) {
    context = context || window;
    const key = Symbol("key");
    context[key] = this;
    const result = context[key](...args);
    delete context[key];
    return result;
};
apply
Function.prototype.myApply = function(context, args) {
    context = context || window;
    args = args || [];
    const key = Symbol("key");
    context[key] = this;
    const result = context[key](...args);
    delete context[key];
    return result;
}
bind
Function.prototype.myBind = function(context, ...args) {
    context = context || window;
    const fn = this;

    return function _fn(..._args) {
        if (this instanceof _fn) {
            return new fn(...args, ..._args); // new 的优先级比bind,call,apply高
        }
        const key = Symbol("key");
        context[key] = fn;
        const result = context[key](...args, ..._args);
        delete context[key];
        return result;
    }
};

10.函数柯里化

函数柯里化的定义:将函数进行转化,返回一个新的函数,如果函数接收的参数等于实际参数时就执行该函数。

const text = function(x1, x2, x3) {
    console.log(x1 + x2 + x3);
};

// 柯里化函数
function curry(fn, ...args) {
    const len = fn.length;
    args = args || [];
    return function(..._args) {
        const params = [...args, ..._args];
        if (params.length < len) {
            return curry.call(this, fn, ...params);
        } else {
            return fn.apply(this, params);
        }
    }
};

const _curry = curry(text);

_curry(1, 2, 3);
_curry(1, 2)(3);
_curry(1)(2)(3);

11.数组扁平化

数组的扁平化就是将一个多维的数组转为一个一维的数组。数组扁平化的方法如下:

  1. 使用es6新增的flat函数。
  2. 如果数组中全为数字可以使用arr.toString().split(",").map(item => parseFloat(item))。
  3. 可以使用Json.stringfy将数组转为字符串,之后使用正则替换[]在进行字符串的拼接。
  4. 使用递归加reduce:
const arr = [1, [2, [3, [4, 5]]], 6];
const flat = function(arr) {
    return arr.reduce((cur, item) => {
        return cur.concat(Array.isArray(item) ? flat(item) : item);
    }, []);
}
console.log(flat(arr));