前端面试常见算法题(一)

算法题一、用js实现二分查找

1、递归的方式

function binarySearch(arr, target, startIndex, endIndex) {
    const length = arr.length
    if (length === 0) return -1

    // 开始和结束的范围
    if (startIndex === null) startIndex = 0
    if (endIndex === null) endIndex = length - 1

    // 如果start和end相遇,则结束
    if (startIndex > endIndex) return -1

    // 中间位置
    const midIndex = Math.floor((endIndex + startIndex) / 2)
    const midValue = arr[midIndex]

    if (target < midValue) {
        // 目标值较小,则继续在左侧进行查找
        return binarySearch(arr, target, startIndex, midIndex - 1)
    } else if (target > midValue) {
        // 目标值较大,则继续在右侧进行查找
        return binarySearch(arr, target, midIndex + 1, endIndex)
    } else {
        return midIndex
    }
}

2、循环的方式

function binarySearch(arr, target) {
    const length = arr.length
    if (length === 0) return -1

    let startIndex = 0//开始位置
    let endIndex = length - 1//结束位置

    while (startIndex <= endIndex) {
        const midIndex = Math.floor((endIndex + startIndex) / 2)
        const midValue = arr[midIndex]
        if (target < midValue) {
            // 目标值较小,则继续在左侧进行查找
            endIndex = midIndex - 1
        } else if (target > midValue) {
            // 目标值较大,则继续在右侧进行查找
            startIndex = midIndex + 1
        } else {
            return midIndex
        }
    }
    return -1
}

总结:
递归会重复调用函数,造成消耗

  • 递归-代码逻辑更加清晰
  • 非递归性能好
  • 时间复杂度O(logn)-非常快
  • 凡事有二分,时间复杂度必包含O(logn)

算法题二、给一个数组,找出其中和为n的两个元素

有一个递增的数组[1,2,3,4,7,11,15]和一个n为15
数组中有两个数,和是n。即4+11 ===15

常规思路:
嵌套循环,找到一个数,然后去遍历下一个数,求和,判断
时间复杂度是O(n^2),不可用

function findTowNumbers1(arr, n) {
    const length = arr.length
    const res = []
    if (length === 0) return res

    for (var i=0;i<length-1;i++){
        const n1 = arr[i]
        let flag = false
        for(var j = i+1;j<length;j++){
            const n2 = arr[j]
            if(n1+n2===n){
                res.push(n1)
                res.push(n2)
                flag = true
                break
            }

        }
        if(flag) break
    }
    return res
}

利用递增 ( 有序 )的特性
随便找两个数如果和大于n,则需要向前寻找,二分法如果和小于n,则需要向后寻找

双指针,时间复杂度降低到O(n)
定义i指向头,j指向尾,求arr[i]+arr[j]
如果大于n,则j需要向前移动,
如果小于n,则i需要向后移动

function findTowNumbers2(arr,n){
    const length = arr.length
    const res = []
    if (length === 0) return res

    let i = 0
    let j = length-1
    while(i<j){
        const n1 = arr[i]
        const n2 = arr[j]
        const sum = n1+n2
        if(sum>n){
            j--
        }else if(sum<n){
            i++
        }else{
            res.push(n1)
            res.push(n2)
            break
        }

    }
    return res
}

算法题三、求一个二叉搜索树的第k小值

在这里插入图片描述
二叉树遍历(递归)
1、前序遍历:root->left->right
2、中序遍历:left->root->right
3、后序遍历:left->right->root

const tree = {
    value:5,
    left:{
        value:3,
        left:{
            value:2,
            left:null,
            right:null
        },
        right:{
            value:4,
            left:null,
            right:null
        }
    },
    right:{
        value:7,
        left:{
            value:6,
            left:null,
            right:null
        },
        right:{
            value:8,
            left:null,
            right:null
        }
    }
}
// 前序遍历
function preOrderTraverse(node){
    if(node===null) return
    console.log(node.value)
    preOrderTraverse(node.left)
    preOrderTraverse(node.right)
}
preOrderTraverse(tree)//5 3 2 4 7 6 8

// 中序遍历
function inOrderTraverse(node) {
    if (node === null) return
    inOrderTraverse(node.left)
    console.log(node.value)
    inOrderTraverse(node.right)
}
inOrderTraverse(tree)//2 3 4 5 6 7 8

// 后序遍历
function postOrderTraverse(node) {
    if (node === null) return
    postOrderTraverse(node.left)
    postOrderTraverse(node.right)
    console.log(node.value)
}
postOrderTraverse(tree)//2 4 3 6 8 7 5

function getKthValue(tree,n){
    inOrderTraverse(tree)
    return arr[n]||null
}
console.log(getKthValue(tree,3))//5

总结:
1、二叉搜索树的特点:left<=root;right>=root
2、二叉搜索树的价值:可使用二分法进行快速查找

算法题四、斐波那契数列

用js计算斐波那契数列第n个值
注意时间复杂度

//递归方式
function fibonacci(n){
    if(n<=0)return 0
    if(n===1)return 1
    return fibonacci(n-1)+fibonacci(n-2)
}
console.log(fibonacci(8))//21

在这里插入图片描述
优化,不用递归用循环
记录中间的结果
时间复杂度为O(n)

function fibonacci2(n) {
    if (n <= 0) return 0
    if (n === 1) return 1

    let n1 = 1//记录n-1的结果
    let n2 = 0//记录n-2的结果
    let res = 0 //记录结果
    
    for (var i = 2; i <= n; i++) {
        res = n1 + n2

        // 记录中间结果
        n2 = n1
        n1 = res
    }
    return res
}
console.log(fibonacci2(8))//21

注意:动态规划
把一个大问题,拆解为多个小问题,逐级向下拆解
用递归的思路去分析问题,再改为循环来实现
算法三大思维:贪心、二分、动态规划

青蛙连环跳
一只青蛙,一次可以跳一级台阶,也可以跳二级台阶
请问青蛙跳到n级台阶,总共有多少种方式
用动态规划来分析问题:要跳到1级台阶,就1种方式f(1) =1
要跳到2级台阶,就2种方式f(2) = 2
要跳到n级台阶,就f(n) = f(n-1)+f(n-2)

算法题五、将数组中所有的0移动到末尾

如:
输入[1,0,3,0,11,0],输出[1,3,11,0,0,0]
只移动0,其他顺序不变
必须在原数组进行操作
传统思路:遍历数组,遇到0则push到数组末尾,通过是splice截取当前元素,时间复杂度为O(n^2)—不可取(splice改变数组导致性能问题)

function moveZero(arr){
    if(arr.length===0)return
    let length = arr.length
    let zeroLength = 0
    for(var i=0;i<length-zeroLength;i++){
        if(arr[i]===0){
            arr.push(0)
            arr.splice(i,1)//本身就有O(n)的时间复杂度
            i--//数组截取了一个元素,i要递减,否则连续0就有错误
            zeroLength++//累加0的长度
        }
    }
}

var arr=[1,0,3,0,11,0]
moveZero(arr)
console.log(arr)//[1,3,11,0,0,0]

优化思路:双指针
定义j指向第一个0,i指向j后面的第一个非0
交换i,j的值,继续向后移动
只遍历一次,所以时间复杂度为O(n)
注意:凡事嵌套循环,是不是可以考虑双指针来完成

function moveZero2(arr){
    if(arr.length===0)return
    let length = arr.length

    let i//j后面的第一个非0
    let j = -1//j指向第一个0
    for(i=0;i<length;i++){
        if(arr[i]===0){
            if(j<0){
                j = i
            }
        }

        if(arr[i]!==0&&j>=0){
            let n = arr[i]
            arr[i] = arr[j]
            arr[j] = n

            j++
        }
    }
}

var arr=[1,0,3,4,0,11,0]
moveZero2(arr)
console.log(arr)//1,3,4,11,0,0,0

注意:数组是连续存储,要慎用splice、unshift等API

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值