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

算法题六:字符串中连续最多的字符,以及次数

如:输入‘abbcccddeeee1234’,计算得到:连续最多的字符是e,4次
传统思路:嵌套循环,找出每个字符的连接次数,并记录,看似简单,时间复杂度是O(n^2)
真实的时间复杂度是O(n),因为有跳步

function findFun(str) {
    var res = {
        char: "",
        length: 0
    }
    const length = str.length
    if (length === 0) return res

    for (var i = 0; i < length; i++) {
        let temLength = 0
        for (j = i; j < length; j++) {
            if (str[i] === str[j]) {
                temLength++
            }
            if (str[i] !== str[j]) {
                if (temLength > res.length) {
                    res.char = str[i]
                    res.length = temLength
                }
                if (i < length - 1) {
                    i = j - 1//跳步
                }
                break
            }
        }
    }
    return res
}

console.log(findFun('abbcccddeeee1234'))//{char:'e',length:4}

优化思路:双指针
定义指针i和j。j不动,i继续移动
如果i和j的值一直相等,则i继续移动
直到i和j的值不相等,记录处理,让j追上i。继续上一步

function findfun2(str) {
    var res = {
        char: "",
        length: 0
    }
    const length = str.length
    if (length === 0) return res

    let i = 0
    let j = 0
    let temLength = 0
    for (; i < length; i++) {
        if (str[i] === str[j]) {
            temLength++
        }
        if (str[i] !== str[j] || i === length - 1) {
            if (temLength > res.length) {
                res.char = str[j]
                res.length = temLength
            }
            temLength = 0
            if (i < length - 1) {
                j = i
                i--
            }
        }
    }
    return res
}
console.log(findfun2('abbcccddeeee1234'))//{char:'e',length:4}

正则表达式—效率非常低,慎用
累计各个元素的连续长度,最后求最大值,徒增空间复杂度
注意:算法题尽量用“低级”代码,慎用语法糖或者高级api

算法题七: 用js实现快速排序,并且说明时间复杂度

固定算法,固定思路
找到中间位置midValue
遍历数组,小于midValue放在left,否则放在right
继续递归,最后concat拼接,返回

获取midValue方式:
1、使用splice,会修改原数组
2、使用slice,不会修改原数组—推荐使用

//splice
function quickSort1(arr){
    const length = arr.length
    if(length===0)return arr

    const midIndex = Math.floor(length/2)
    const midValue = arr.splice(midIndex,1)[0]

    const left = []
    const right = []

    for(let i = 0;i<arr.length;i++){
        if(arr[i]<midValue){
            left.push(arr[i])
        }else if(arr[i]>midValue){
            right.push(arr[i])
        }
    }
    return quickSort1(left).concat([midValue],quickSort1(right))
}

console.log(quickSort1([2,8,4,5,6,9,1,3,7]))//1,2,3,4,5,6,7,8,9

//slice
function quickSort2(arr) {
    const length = arr.length
    if (length === 0) return arr

    const midIndex = Math.floor(length / 2)
    const midValue = arr.slice(midIndex, midIndex + 1)[0]

    const left = []
    const right = []

    for (let i = 0; i < length; i++) {
        if (i !== midIndex) {
            if (arr[i] < midValue) {
                left.push(arr[i])
            } else if (arr[i] > midValue) {
                right.push(arr[i])
            }
        }

    }
    return quickSort1(left).concat([midValue], quickSort1(right))
}

console.log(quickSort2([2, 8, 4, 5, 6, 9, 1, 3, 7]))

有遍历,有二分----时间复杂度:O(nlogn)或O(nlogn)
常规排序,嵌套循环,复杂度是O(n^2)
splice 和 slice 没有区分出来
算法本身的复杂度就够高O(n
logn),外加上,splice是逐步二分之后执行的,二分会快速消减数量级
如果单独比较splice和slice,效果会很明显

算法题八:求对称数

求1-10000之间的所有对称数(回文)
例如:0,1,2,11,22,101,232,1221…
思路一:使用数组翻转、比较
数字转换为字符串,再转换为数组,数组reverse,再join为字符串,前后字符串进行对比

function findPalindromeNumbers1(max){
    const res = []
    if(max<=0)return res

    for(var i=0;i<=max;i++){
        var s = i.toString()
        if(s === s.split('').reverse().join('')){
            res.push(i)
        }
    }
    return res
}
console.info(findPalindromeNumbers1(200)) //[0,1,2,3,4,5,6,7,8,9,11,22,33,44,55,66,77,88,99,101,111,121, 131,141,151, 161, 171,181,191]

思路2-字符串头尾比较
数字转换为字符串,字符串头尾字符比较(也可以用栈,像括号匹配,但是要注意奇偶数)

function findPalindromeNumbers2(max) {
    const res = []
    if (max <= 0) return res

    for (let i = 0; i <= max; i++) {
        let s = i.toString()
        let length = s.length
        let startIndex = 0
        let endIndex = length - 1

        var flag = true
        while (startIndex < endIndex) {
            if (s[startIndex] !== s[endIndex]) {
                flag = false
                break
            } else {
                startIndex++
                endIndex--
            }
        }
        if (flag) {
            res.push(i)
        }

    }

    return res
}
console.info(findPalindromeNumbers2(200)) //[0,1,2,3,4,5,6,7,8,9,11,22,33,44,55,66,77,88,99,101,111,121, 131,141,151, 161, 171,181,191]

思路3-生成翻转数
使用%和Math.floor生成反转数
前后数字进行对比(看起来比较复杂)

性能分析
思路1-看似是O(n),但数组转换、操作都需要时间,所以慢
思路2vs思路3,操作数字更快(电脑原型就是计算器)
思路2要用栈,不合适。因为栈也一般用数组实现,比较慢

注意:
1、尽量不用转换数据结构,尤其数组这种有序结构
2、尽量不要用内置API,如reverse,不好识别复杂度
3、数字操作最快,其次是字符串

算法题九:高效的字符串前缀匹配

有一个英文单词库(数组),里面有几十万个英文单词
输入一个字符串,快速判断是不是某个单词的前缀

思路:
英文字母一共26个,可以提前把单词库数组拆分为26个
既然第一层拆分为26个,第二层、第三层,还可以继续拆分
最后把单词库拆分为一棵树

性能分析:
如遍历数组,时间复杂度至少O(n)起步(n是数组长度)
而改为树,时间复杂度降低到 O(m) (m 是单词的长度)
Ps:哈希表(对象)通过key 查询,时间复度是O(1)

注意:
考虑优化原始数据结构(需和面试官沟通确认)
有明确范围的数据(如26个英文字母),考虑使用哈希表(对象)

算法题十:数字千分位格式化

将数字千分位格式化,输出字符串
如输入数字12050100,输出字符串12,050,100

常见思路:转换为数组,reverse,每3位拆分
使用正则表达式
使用字符串进行拆分

//使用数组
function format1(n){
    n = Math.floor(n)//只考虑整数

    const s = n.toString()
    const arr = s.split('').reverse()
    console.log(arr)
    return arr.reduce((pre,val,index)=>{
         if(index%3===0){
             if(pre){
                 return val+','+ pre
             }else{
                 return val
             }
             
         }else{
             return val+ pre
         }
    },'')

}

console.log(format1(243244500))//243,244,500

function format2(n) {
    n = Math.floor(n)//只考虑整数
    const s = n.toString()
    const length = s.length
    let res = ""
    for (var i = length-1; i >= 0; i--) {//i=6 i=5
        let j = length - i//j=1 j=2
        if (j % 3 === 0) {
            if (i === 0) {
                res = s[i] + res
            } else {
                res = "," + s[i] + res
            }
        } else {
            res = s[i] + res
        }
    }
    return res
}

console.log(format2(1023040))//1,023,040

性能分析
使用数组,转换影响性能
使用正则表达式,性能较差
使用字符串,性能较好-推荐

算法题十一:切换字母大小写

输入一个字符串,切换其中字母的大小写
如,输入字符串传12aBc34,输入字符串12ABC34
常见思路:正则表达式,
通过ASCII编码

function switchLetterCase1(str){
    let length = str.length
    const reg1 = /[a-z]/
    const reg2 = /[A-Z]/
    let res = ''

    for(var i=0;i<length;i++){
        let c = str[i] 
        if(reg1.test(c)){
            res+=c.toUpperCase()
        }else if(reg2.test(c)){
            res+=c.toLowerCase()
        }else{
            res+=c
        }
    }
    return res
}
console.log(switchLetterCase1('Aan2132/3Uye'))//aAN2132/3uYE

function switchLetterCase2(str){
    let length = str.length
    let res = ''
    for(var i=0;i<length;i++){
        let c = str[i] 
        let code = c.charCodeAt(0)
        if(code>=65&&code<=90){
            res+=c.toLowerCase()
        }else if(code>=97&&code<=122){
            res+=c.toUpperCase() 
        }else{
            res+=c
        }
    }
    return res
}
console.log(switchLetterCase2('Aan2132/3Uye'))//aAN2132/3uYE

0.1+0.2!==0.3

计算机使用二进制存储数据
整数转换二进制没有误差,如9转换为二进制是1001
而小数可能无法用二进制准确表达,如0.2转换为1.1001100…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值