zoukankan      html  css  js  c++  java
  • js常用算法

    1.判断一个字符串是“回文”类型,回文:形如‘abcba’、‘mamam’这种第一个与最后一个字符相同,第二个和倒数第二个字符相同。。。一次类推,该怎么实现呢?

    对与我来说,首先看到第一眼,还真没想起来怎么处理,后来想到用reverse()就可以啊!MD!那是数组的方法!!!总之要用reverse()方法,那就先转成数组,再转成字符串来比较就行了啊

    function checkReverseString(str) {  
        return str == str.split('').reverse().join('');
    }

    2.数组去重:
    先来看用老方法处理:

    1).object对象中是否存在当前key

    var arr = [1,2,3,1,2,3]
    function uniqueArr(arr) {
        var obj = {}
        var result = []
        for (var i = 0, len = arr.length; i < len; i++) {
            if (!obj[arr[i]]) {
                obj[arr[i]] = true
                result.push(arr[i])
            }
        }
        return result
    }

    2) Array.indexOf()

    var arr = [1,2,3,1,2,3]
    function uniqueArr(arr) {
        var obj = arr
        var result = []
        for (var i = 0, len = arr.length; i < len; i++) {
            if (result.indexOf(arr[i]) === -1) {
                result.push(arr[i])
            }
        }
        return result
    }

    3) ES5 filter 

    [1,1,2,2,3,'a','a'].filter(function(ele,index,array){
        return index === array.indexOf(ele)
    })

    indexOf(ele)检查到的是第一次遇到当前项的下标,我们看到数字1,出现了两次次,0位置和1位置,indexOf(1) 永远都等于0,所以1位置的1就不符合filter传入的规则,只返回0位置的1.

    4)ES5 reduce(fn(), el, index, array)

    [1,1,2,3].reduce((result, el, i, arr) => {
        if (i === arr.indexOf(el)) {
            result.push(el)
        }
        
        return result
    }, [])

    也是循环每一项,每一项的处理都在内部函数中处理。

    5) ES6 Array.from(new Set()),set中不存在重复的项:

    var arr = [1,2,3,1,2,3]
    var arr1 = Array.from(new Set(arr))
    console.log(arr1)

    还是这种方法好啊,清清爽爽!

    3.统计一个字符串中出现次数最多的字符和出现的次数

    刚开始没有太多的思路,刚才数组去重,用到了obj key的属性,那这里是不是也可以用呢?遇到新的字符,obj的该字符的值设为1,再遇到就+1,来试试:

    var str = 'aaaabbbc'
    function maxSym(str) {
        if (str && str.length <= 1) {
            return str
        }
        var obj = {}
        for (var i = 0, len = str.length; i < len; i++) {
            obj[str[i]] = obj[str[i]] ? obj[str[i]] + 1 : 1
                  // 这里只能写obj[str[i]] + 1,其他写法,例如++,执行会有问题,如果你知道为什么请告诉我
        }
        var maxStr = ''
        var maxCount = 0
        for (var key in obj) {
            if (obj[key] > maxCount) {
                maxStr = key
                maxCount = obj[key]
            }
        }
        return maxStr + ':' + maxCount
    }
    console.log(maxSym(str))

    4.不愿意看到的排序,还是要来的

    1).先看看冒泡排序,必须要掌握

    maopaopaixu

    var arr = [2,34,4,1]
    function bubbleSort(arr) {
        if (arr && arr.length <= 1) {
            return arr
        }
        for (var i = 0, len = arr.length; i < len - 1; i ++) { // 控制循环的轮数,为什么要len - 1?要比较有两个元素的数组[2,1],请问比较几轮?1轮!以此类推:len-1
            for (var j = 0; j < len - i - 1; j ++) { // 控制每轮要比较的次数
                if (arr[j] > arr[j+1]) {
                    var temp = arr[j]
                    arr[j] = arr[j+1]
                    arr[j+1] = temp
                }
            }
            console.log(arr)
        }
        return arr
    }
    console.log(bubbleSort(arr))

    2). 选择排序:

    和冒泡排序原理差不多,但不是两两比较,立即换位置,而是执行完一轮,再互换位置:

    [3,4,5,1,7],假设第0位3是最小的数,与后一位比较3 < 4,再往后 3 < 5, 再往后 3 > 1, 到这里,最小的数的位置应该变成第3位,把这个位置记下来,然后继续往后比较 1 < 7,到这里就比较结束了,现在需要互换位置了,现在最小的数的位置在第3位,应该转换成[1,4,5,3,7],也就是“假定最小的”和真实最小的互换位置。第0位已经是最小的数了。第二轮,从第1位开始算起,以此类推,完成排序

    function selectionSort(arr) {
        var len = arr.length, min;
        for (var i = 0; i < len; i ++) {
            min = i // 假定第i轮循环的第一个数最小,把这个位置保存起来
            for (var j = i + 1; j < len; j ++) {
                if (arr[j] < arr[min]) {
                    min = j // 如果后面有更小的数,把这个位置记为最小
                }
            }
            if (i != min) { // 如果后面有更小的数,第i轮第一个数要和更小的数互换位置
                var temp = arr[min]
                arr[min] = arr[i]
                arr[i] = temp 
            }
        }
        return arr
    }
    var arr = [3,4,5,1,7]
    console.log(selectionSort(arr))

    过程: 

    [3,4,5,1,7] => [1,4,5,3,7] => [1,3,5,4,7] => [1,3,4,5,7] => [1,3,4,5,7]

    3).插值排序:(斗地主的时候,左手里面的牌,是不是排好序的?右手随意起牌,差到左手里已经排好序的牌中,这个过程就是插值排序)

    function inputSort(data)  {  
        var temp; 
        for (var i = 1; i < data.length; i++) {  
            for(var j = i; (j > 0) && (data[j] < data[j - 1]); j--) {  
                temp = data[j]; 
                data[j] = data[j - 1]
                data[j - 1] = temp
            }
        }
        return data
    }
    var arr = [4,3,2,5,1]
    console.log(inputSort(arr))

    4)合并排序,MD,老菜鸟,写不动了,先到这里,让我好好理解一下

     归并排序,采用了‘分’,分而治之,然后再合。

    // 先看把两个有序数组合并
    function merge(left, right) {
        var result = []
        while (left.length > 0 && right.length > 0) {
            if (left[0] < right[0]) { // 左边数组的第一个数值,小于右边数组第一个数值
                result.push(left.shift()) // 把左边数组第一个数值取出来,并添加到result中
            } else {
                result.push(right.shift()) // 否则把右边数组第一个数值取出来,并添加到result中
            }
        }
        // 比如 left = [1, 2] right = [3, 4] ,
        // 只发生两次比较,第一次: 1 < 3 , result = [1]
        // 第二次: 2 < 3 , result = [1, 2]
        // 这个时候left已经空了,length => 0 , 所以while 循环结束,但是right = [3, 4]
        // 当然也可能是right先变成[],
        // 所以要把result、left和right拼接在一起返回,得到最终结果
        return result.concat(left, right)
    }
    
    // 这里的主要功能是拆分数组,至少拆成两个数组,才能使用merge方法,所以使用递归,一直拆下去,
    // 当数组的长度小于等于1的时候,终止递归拆分,执行merge操作,并返回merge后的结果
    // 递归调用的逻辑,可能会比较绕,多理解一下,还是没大问题的!加油!
    function mergeSort(arr) { if (arr.length <= 1) { return arr } var middle = Math.floor(arr.length / 2) // 取中间序列号 var left = arr.slice(0, middle) // 取中间序列号之前的部分,赋值给left var right = arr.slice(middle) // 取中间序列号之后的部分,赋值给right return merge(mergeSort(left), mergeSort(right)) // 执行merge操作,其实这里面是先把大数组递归拆成单元素数组,再merge } var arr = [3,1,4,2,6] console.log(mergeSort(arr))

    结果:

    5).快速排序,先找到一个中间值,然后遍历,把比中间值小的放在左边数组,把比中间值大的放在右边数组,然后再分别按这种思路对两个新数组比较,以此类推,直到结束。

    var arr = [2,34,3,4]
    function quickSort(arr) {
        if (arr && arr.length <= 1) {
            return arr
        }
        var midEl = arr[0] // 
        var left = []
        var right = []
        for (var i = 1, len = arr.length; i < len; i++) {
            if (arr[i] < midEl) { 
                left.push(arr[i]) // 比第一个数小的数放在左边
            } else {
                right.push(arr[i]) 
            }
        }
        return quickSort(left).concat([midEl], quickSort(right))
    }
    console.log(quickSort(arr))
    这种方式会额外增加两个数组,空间复杂度高,下面说一个更节省空间的写法,必须掌握。
    function quickSort(arr) {
        if (arr.length <= 1) { // 数组长度为1时,默认数组有序
            return arr
        }
    
        let flag = arr[0] // 取第0个元素为 中间值(当然你可以随便取,比如取最后一个)
        let i = 1 // 令左边指针初始化为1
        let j = arr.length - 1 // 令右边指针初始化为 数组长度-1
    
        while(i < j) { // 如果左指针还小于又指针,则继续判断
            while(arr[j] >= flag && i < j) { // 如果 右边 的数大于 flag , 不做操作,继续左移,因为大数本来就该在右边
                j--
            }
            while(arr[i] <= flag && i < j) { // 如果 左边 的数小于 flag, 不做操作,继续右移,因为小数本来就该在左边
                i++
            }
            let temp = arr[j] // 如果不满足上面两个条件,也就是说,左边出现了大数,且右边出现了小数,则执行交换
            arr[j] = arr[i]
            arr[i] = temp
        }
        let temp = arr[0] // 最后调换 中间值 与 右指针(也是左指针,因为此时左右指针索引相等),到此第一次遍排序结束
        arr[0] = arr[j]
        arr[j] = temp
        return quickSort(arr.slice(0,i)).concat([flag]).concat(quickSort(arr.slice(j+1))) // 递归调用即可实现整体排序
    }
    var a = [13,1,3,45,2,56,6,10]
    var b = quickSort(a)
    console.log(b)
     

    结果:

     5、递归:求和问题、(待补充其他类型)

    function fn(n) {
        if (n === 1) return 1
        return n + fn(n - 1)
    }

     function fn(n) {
      return n && n + fn(n - 1)
     }

    fn(5) // 15

    6、产生斐波那契数列,并用canvas画图,[0, 1, 1, 2, 3, 5, 8, 13, 21, 34...],数组中的每一项当做圆的半径,每次画1/4圆。

    var canvas = document.getElementsByTagName('canvas')[0]
    canvas.width = 600
    canvas.height = 480
    var ctx = canvas.getContext('2d')
    var coor = {x: 300, y: 240}
    function draw(r1, n, r2) { // r1-前一个圆的半径,n-数组下标,r2-下标圆的半径
        var r = r2 * 5 // 半径太小,放大5倍
        var startAngle = Math.PI // 起始角
        var endAngle = Math.PI * 0.5 // 终止角
        var antiClockWise = true // 逆时针方向
      // 两内切圆满足的条件: 1、两圆的圆心距d,等于大圆的半径r2减去小圆的半径r1,即: d = r2 - r1.
      // 依据上述条件,可以得出下一个圆的圆心坐标:注意,下一个圆的坐标值,有一个值和当前圆的一致,因为无论哪种情况,两圆心都会在一条直线上。
      // 所以有一下结论: x2 = x1 +(-) d;y2 = y1 +(-) d => x2 = x1 +(-) (r2 - r1);y2 = y1 +(-) (r2 - r1)
    if (n > 2) { switch(n % 4) { case 0: // n = 4、8、12...第一象限 coor.x = coor.x - (r - (r1 * 5)) // 这里刚开始会比较难理解,注意对照上面的推导关系,会好理解一些. *5 是因为r = r2 * 5,所以
                               // r1也放大5倍 startAngle
    = 0 endAngle = Math.PI * 1.5 break; case 1: // n = 5、9、13...第二象限 coor.y = coor.y + (r - (r1 * 5)) startAngle = Math.PI * 1.5 endAngle = Math.PI break; case 2: // n = 6、10、14...第三象限 coor.x = coor.x + (r - (r1 * 5)) startAngle = Math.PI endAngle = Math.PI * 0.5 break; case 3: // n = 3、7、11...第四象限 coor.y = coor.y - (r - (r1 * 5)) startAngle = Math.PI * 0.5 endAngle = 0 break; } } ctx.beginPath() ctx.arc(coor.x, coor.y, r, startAngle, endAngle, antiClockWise) ctx.lineWidth = 3 ctx.strokeStyle = "#f34e56" ctx.stroke() } function getFibonacci(n) { var i = 0 var arr = [] while(i < n) { if (i <= 1) { arr.push(i) } else { arr.push(arr[i - 2] + arr[i - 1]) // arr[i] = arr[i - 2] + arr[i - 1] } i++ } return arr } var arrFibonacci = getFibonacci(10) console.log(arrFibonacci) for (var j = 0; j < arrFibonacci.length; j++) { if (j >= 2) { // 数组长度至少为3,才满足斐波那契数组定义 draw(arrFibonacci[j - 1], j, arrFibonacci[j]) } }

    结果如图:

    7、合并两个有序数组:

    var a = [2,4,6], b = [1,3]
    function two(left = [], right = []) {
        var result = []
        while(left.length && right.length) {
            result.push(left[0] <= right[0] ? left.shift() : right.shift())
        }
        return result.concat(left, right)
    }
    

      

  • 相关阅读:
    无服务计算应用场景探讨及 FaaS 应用实战
    从零入门 Serverless | Serverless 应用如何管理日志 & 持久化数据
    实验楼流程
    go beego框架与python实现数据交互
    golang 并发运算时主线程先运行完,子线程运行没结束的问题记录
    滑动窗口解决最小子串问题 leetcode3. Longest Substring Without Repeating Characters
    golang 遍历树状结构
    golang 三维向量相关操作
    golang 矩阵乘法、行列式、求逆矩阵
    golang float32/64转string
  • 原文地址:https://www.cnblogs.com/whq920729/p/10666522.html
Copyright © 2011-2022 走看看