js常用算法

Posted whq920729

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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)
}

  

 

以上是关于js常用算法的主要内容,如果未能解决你的问题,请参考以下文章

javascript JS-常用代码片段

js 常用代码片段

前端开发常用js代码片段

JS常用代码片段2-值得收藏

JS常用代码片段2-值得收藏

JS常用代码片段-127个常用罗列-值得收藏