简单算法

2021-05-18  本文已影响0人  欢欣的膜笛

实现 trim

String.prototype.trim = function() {
    return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}

斐波那契数列

斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0, F(1) = 1, F(N) = F(N - 1) + F(N - 2), 其中 N > 1,给定 N,计算 F(N)。

function fib(N) {
    // 若 N <= 1,则返回 N
    if (N <= 1) {
        return N;
    }
    // 若 N == 2
    if (N == 2) {
        return 2;
    }
    // 至少需要三个变量存储 fib(N), fib(N-1) 和 fib(N-2)。
    let current = 0; // 代表 fib(N)
    let prev1 = 2; // 代表fib(N-1)
    let prev2 = 1; // 代表 fib(N-2)

    for (let i = 3; i <= N; i++) {
        current = prev1 + prev2;
        prev2 = prev1;
        prev1 = current;
    }
    return current;
}

时间复杂度:O(N)。
空间复杂度:O(1),仅仅使用了 current,prev1,prev2。

删除字符串中的所有相邻重复项

const removeDuplicates = function(s) {
    if (!s || s.length === 1) {
        return s || ''
    }
    const stock = []
    for (const item of s) {
        if (stock.length && item === stock[stock.length - 1]) {
            stock.pop()
        } else {
            stock.push(item)
        }
    }
    return stock.join('')
};
console.log(removeDuplicates('abbaca')) // ca

版本号排序

var versions = ["1.45.0", "1.5", "6", "3.3.3.3.3.3.3"];
// 要求从小到大排序,注意'1.45'比'1.5'大
function sortVersion(versions) {
// TODO
}
// => ['1.5','1.45.0','3.3.3.3.3.3','6']

const sortVersion = (list) => {
    if (!list || !list.length) {
        return
    }
    return list.sort((a, b) => {
        let aArr = a.split('.')
        let bArr = b.split('.')
        let maxLen = aArr >= bArr ? aArr : bArr
        for (let i = 0; i < maxLen; i++) {
            let x = aArr[i] || 0
            let y = bArr[i] || 0
            if (x - y !== 0) {
                return x - y
            }
        }
    })
}

打乱数组

打乱一个没有重复元素的数组
洗牌算法:从最后一个元素开始,从数组中随机选出一个位置,交换,直到第一个元素。

class Disorder {
    constructor(arr) {
        this.originArr = [ ...arr ]
    }

    reset() {
        return this.originArr
    }

    shuffle() {
        const shuffleArr = [ ...this.originArr ]
        let curLen = shuffleArr.length - 1
        while (curLen > -1) {
            // 生成一个范围在当前下标到数组末尾元素下标之间的随机整数
            const random = Math.floor(shuffleArr.length * Math.random())
            console.log(random);
            // 将当前元素和随机选出的下标所指的元素互相交换
            [ shuffleArr[curLen], shuffleArr[random] ] = [ shuffleArr[random], shuffleArr[curLen] ]
            curLen--
        }
        return shuffleArr
    }
}

var obj = new Disorder([ 1, 4, 8, 9 ])
console.log(obj.shuffle());
console.log(obj.reset());

二叉树的最大深度

给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

function TreeDepth(pRoot) {
    if (!pRoot) {
        return 0;
    }
    let left = TreeDepth(pRoot.left);
    let right = TreeDepth(pRoot.right);
    return Math.max(left, right) + 1;
}

二叉树的镜像

操作给定的二叉树,将其变换为源二叉树的镜像。

function Mirror(pRoot) {
    if (!pRoot) {
        return null
    }
    let temp = pRoot.left
    pRoot.left = pRoot.right
    pRoot.left = temp

    Mirror(pRoot.left)
    Mirror(pRoot.right)

    return pRoot
}

树的子结构

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

// 比较两棵树是否相同
function compTree(root1, root2) {
    if (!root2) {
        return true
    }
    if (!root1) {
        return false
    }
    if (root1.val !== root2.val) {
        return false
    }
    return compTree(root1.left, root2.left) && compTree(root1.right, root2.right)
}

// 判断是否子结构
function HasSubtree(pRoot1, pRoot2) {
    if (!pRoot1 || !pRoot2) {
        return false
    }
    if (!compTree(pRoot1, pRoot2)) {
        return HasSubtree(pRoot1.left, pRoot2) || HasSubtree(pRoot1.right, pRoot2)
    }
    return true
}

给一个嵌套对象,返回展平对象(属性为 a.b.c)

const obj = {
    a: 'a',
    b: [1, 2],
    c: true,
    d: function() { console.log('d') },
    e:  {
        a: 'a-e',
        b: [1, 2, 3],
        c: false,
        d: function() { console.log('e-d') },
        f:  {
            a: 'a-f',
            b: [1, 2, 4],
            c: true,
            d: function() { console.log('f-d') },
        }
    }
}

function flatObj (data) {
    if (!data || data.constructor !== Object) {
        return {}
    }
    const obj = {}
    function flat(data, baseKey) {
        // for...in 会遍历原型链上所有属性,不推荐使用
        Object.keys(data).forEach(key => {
            const value = data[key];
            const finalKey = baseKey ? `${baseKey}.${key}` : key;
            if (value.constructor === Object) {
                flat(value, finalKey);
            } else {
                obj[finalKey] = value;
            }
        })
        return obj
    }
    return flat(data);
}

const data = flatObj(obj)
data.d()
data['e.d']()
data['e.f.d']()

计算一个有序数组中,某个数字出现的次数

  1. 方法1:用两个循环,一个从头找一个从尾找,找到第一次出现的位置和最后一次出现的位置。再相减即可。
function GetNumberOfK(data, k) {
    var start = -1
    var end = -1
    for (var i = 0; i < data.length; i++) {
        if (data[i] == k) {
            start = i;
            break
        }
    }
    for (var j = data.length - 1; j > -1; j--) {
        if (data[j] == k) {
            end = j
            break
        }
    }
    if (start != -1 && end != -1) {
        return end - start + 1;
    } else {
        return 0
    }
}
  1. 方法2:
    二分法,找到数字出现的第一次和最后一次。

function GetNumberOfK(data, k) {
    if (data.length == 0) {
        return 0;
    }
    var first = theFirst(data, 0, data.length - 1, k)
    var last = theLast(data, first - 1, data.length - 1, k)
    if (first != -1 && last != -1) {
        return last - first + 1;
    } else {
        return 0;
    }
}
// 找到第一次出现K的位置
function theFirst(data, start, end, k) {
    if (start > end) {
        return -1;
    }
    var mid = parseInt((start + end) / 2)
    if (data[mid] > k) {
        end = mid - 1;
        return theFirst(data, start, end, k)
    } else if (data[mid] < k) {
        start = mid + 1
        return theFirst(data, start, end, k)
    }
    // 还要多加一个判断如果mid-1也为k的话,说明第一次出现的位置还更小。
    else if ((mid - 1 >= 0) && (data[mid - 1] == k)) {
        return theFirst(data, start, mid - 1, k)
    } else {
        return mid;
    }
}
// 找到最后一次出现K的位置
function theLast(data, start, end, k) {
    if (start > end) {
        return -1;
    }
    var mid = parseInt((start + end) / 2)
    if (data[mid] > k) {
        end = mid - 1;
        return theLast(data, start, end, k)
    } else if (data[mid] < k) {
        start = mid + 1
        return theLast(data, start, end, k)
    }
    // 还要多加一个判断如果mid+1也为k的话,说明最后一次出现的位置还更大。
    else if ((mid + 1 < data.length) && (data[mid + 1] == k)) {
        return theLast(data, mid + 1, end, k)
    } else {
        return mid;
    }
}

传入真实 dom,输出 json

const handleNode = (node) => {
    if (!node) {
        return
    }
    return {
        name: node.nodeName,
        nodes: node.children && node.children.length ?
            Array.from(node.children).map(item => handleNode(item)) :
            node.innerHTML
    }
}
console.log(handleNode(document.body));

一个数组,获取出现次数最多的元素及其次数

const calcMaxNum = arr => {
    if (!Array.isArray(arr) || !arr.length) {
        return
    }
    const map = new Map()
    let maxItem = null
    let maxNum = 0
    arr.forEach(item => {
        if (map.has(item)) {
            map.set(item, map.get(item) + 1)
        } else {
            map.set(item, 1)
        }
        if (map.get(item) > maxNum) {
            maxItem = item
            maxNum = map.get(item)
        }
    })
    return { maxItem, maxNum }
}
calcMaxNum([1, 2, 3, 1, '2', '3', '1', 3, 5, 4, 3])

输入 [ [1, 4, 7], [2, 5, 8], [3, 6, 9]],输出 [ [1, 2, 3], [4, 5, 6], [7, 8, 9]]

方式一:
const fn = (arr) => {
    if (!Array.isArray(arr) || !arr.length) {
        return;
    }
    return Array(arr[0].length).fill('').map((item, index) => arr.map(arrItem => arrItem[index]))
}

// 方式二:
function fn(arr) {
    const newArr = [];
    for (let i = 0; i < arr[0].length; i++) {
        newArr.push([]);
    }

    for (let i = 0; i < arr.length; i++) {
        for (let j = 0; j < arr[0].length; j++) {
            newArr[i][j] = arr[j][i]
        }
    }
    return newArr;
}
fn([[1, 4, 7], [2, 5, 8], [3, 6, 9]])

N[string],表示 string 正好重复 N 次

const decodeStr = function (str) {
    if (typeof str !== "string") {
        throw "请输入字符串";
    }
    let index = 0;
    let decodeQueue = [];
    while (index < str.length) {
        let eleStr = str[index];
        if (eleStr === "[") {
            decodeQueue.push(index);
        }
        if (decodeQueue.length > 0 && eleStr === "]") {
            let leftIndex = decodeQueue.pop();
            let rightIndex = index;
            str = strFormat(str, leftIndex, rightIndex);
            index = 0;
            continue;
        }
        index++;
    }
    return str;
};
const strFormat = function (str, startIndex, rightIndex) {
    // 辅助函数: 将形如str[]对应左右index展开;
    let temp = str.substring(startIndex + 1, rightIndex);
    let copiedStr = "";
    let copyNum = "";
    while (str[--startIndex] >= 0) {
        copyNum = str[startIndex] + copyNum;
    }
    for (let i = 0; i < copyNum; i++) {
        copiedStr += temp;
    }
    str =
        str.substring(0, startIndex + 1) +
        copiedStr +
        str.substring(rightIndex + 1);
    return str;
};
decodeStr('3[abc]2[cd]ff') // 'abcabcabccdcdff'

判断b是否为a的子序列

const isSubsequence = (b, a) => {
    let bi = 0;
    let ai = 0;
    while (bi < b.length) {
        if (a[ai] === b[bi]){
            bi++;
        }
        ai++;
        if (ai > a.length) {
            return false;
        }
    }
    return true;
};
isSubsequence([1, 5, 11], [1, 3, 5, 7, 11]); // true

请完成下列函数

/**
 *
 * @param { Array } urls  请求地址数组
 * @param { Number } max 最大并发请求数
 * @param { Function } callback  回调地址
 */
function parallelFetch(urls, max, callback) {
    // 如果当前环境不支持 fetch , 则提示程序无法正常运行
    if (!window.fetch || "function" !== typeof window.fetch) {
        throw Error("当前环境不支持 fetch 请求,程序终止")
    }
  
    // 如果参数有误,则提示输入正确的参数
    if (!urls || urls.length <= 0) {
        throw Error("urls is empty: 请传入正确的请求地址")
    }
  
    const _urlsLength = urls.length; // 请求地址数组的长度
    const _max = max || 1 // 保证最大并发值的有效性
    let _currentIndex = 0 // 当前请求地址的索引
    let _maxFetch = max <= _urlsLength ? max : _urlsLength // 当前可以正常请求的数量,保证最大并发数的安全性
    let _finishedFetch = 0 // 当前完成请求的数量,用于判断何时调用回调
  
    console.log(`开始并发请求,接口总数为 ${_urlsLength} ,最大并发数为 ${_maxFetch}`)
    // 根据最大并发数进行循环发送,之后通过状态做递归请求
    for (let i = 0; i < _maxFetch; i++) {
        fetchFunc()
    }

    // 请求方法
    function fetchFunc() {
        // 如果所有请求数都完成,则执行回调方法
        if (_finishedFetch === _urlsLength) {
            console.log(`当前一共 ${_urlsLength} 个请求,已完成 ${_finishedFetch} 个`)
            if ("function" === typeof callback) {
                return callback()
            }
            return false
        }
        // 如果当前请求的索引大于等于请求地址数组的长度,则不继续请求
        if (_currentIndex >= _urlsLength) {
            _maxFetch = 0
        }
  
        // 如果可请求的数量大于0,表示可以继续发起请求
        if (_maxFetch > 0) {
            console.log( `当前正发起第 ${_currentIndex + 1 } 次请求,当前一共 ${_urlsLength} 个请求,已完成 ${_finishedFetch} 个,请求地址为:${urls[_currentIndex]}`)
            // 发起 fetch 请求
            fetch(urls[_currentIndex])
                .then((res) => {
                    // TODO 业务逻辑,正常的逻辑,异常的逻辑
                    // 当前请求结束,正常请求的数量 +1
                    _maxFetch += 1
                    _finishedFetch += 1
                    fetchFunc()
                })
                .catch((err) => {
                    // TODO 异常处理,处理异常逻辑
                    // 当前请求结束,正常请求的数量 +1
                    _maxFetch += 1
                    _finishedFetch += 1
                    fetchFunc()
                });
            // 每次请求,当前请求地址的索引  +1
            _currentIndex += 1
            // 每次请求,可以正常请求的数量 -1
            _maxFetch -= 1
        }
    }
}
  
let urls = []
for (let i = 0; i < 100; i++) {
    urls.push(`https://jsonplaceholder.typicode.com/todos/${i}`)
}
const max = 10
const callback = () => {
    console.log("我请求完了")
}
parallelFetch(urls, max, callback)
上一篇下一篇

猜你喜欢

热点阅读