js 常用必备算法_上

64 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情

js 常用必备算法_上

扁平化

数组扁平化就是将一个多维数组转换为一个一维数组,通过递归实现数组扁平化。 Array.isArray() 用于确定传递的值是否是一个 Array

Array.isArray([1, 2, 3]);
// true
Array.isArray({foo: 123});
// false
Array.isArray("foobar");
// false
Array.isArray(undefined);
// false
array.concat(arr)

concat() 方法用于连接两个或多个数组。 该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。

function flatten(arr) {
let result=[]
for (let i=0,len=arr.length;i<len;i++) {
if (Array.isArray(arr[i])) {
result=result.concat(flatten(arr[i]))
} else {
result.push(arr[i])
}
}
return result
}

去重

数组去重,有多种方式,通过 Es6 Set(),实现去重,set 和 map 类似,但不能保存重复的数据

function unique(arr) {
    let appeard=new Set()
    return arr.filter(item=>{
        //创建一个可以唯一标识对象的字符串id
        let id=item+JSON.stringify(item)
        if (appeard.has(id)) {
            return false
        } else {
            appeard.add(id)
            return true
        }
    })
}

浅拷贝

浅拷贝会在栈中开辟另一块空间,并将被拷贝对象的栈内存数据完全拷贝到该块空间中,即基本数据类型的值会被完全拷贝,而引用类型的值则是拷贝了“指向堆内存的地址”。有…扩展运算符、Array.from、Object.assign()方法

function copy(obj) {
 let result=Array.isArray(obj)?[]:{}
 Object.keys(obj).forEach(key=>result[key]=obj[key])
 return result
}
otherStar={...star}
Object.assign({},star)

深拷贝

不仅会在栈中开辟另一块空间,若被拷贝对象中有引用类型,则还会在堆内存中开辟另一块空间存储引用类型的真实数据。 万能转换器:JSON.parse(JSON.stringify(obj))深拷贝已有对象,它可以深拷贝多层级的,不用担心嵌套问题。

function copy(obj,appeard=new Map()) {
 if (!(obj instanceof Object)) return obj//如果是原始数据类型
    if (appeard.has(obj)) return appeard.get(obj)//如果已经出现过
    let result=Array.isArray(obj)?[]:{}
    appeard.set(obj,result);//将新对象放入map
    //遍历所有属性进行递归拷贝
    [...Object.keys(obj),...Object.getOwnPropertySymbols(obj)]
     .forEach(key=>result[key]=copy(obj[key],appeard))
    return result
}
JSON.stringify

只能处理纯 JSON 数据

有几种情况会发生错误

包含不能转成 JSON 格式的数据

循环引用

undefined,NaN, -Infinity, Infinity 都会被转化成 null

RegExp/函数不会拷贝

new Date()会被转成字符串

new=JSON.parse(JSON.stringify(old))

遍历

广度优先和深度优先算法 对于算法来说 无非就是时间换空间 空间换时间 深度优先不需要记住所有的节点, 所以占用空间小, 而广度优先需要先记录所有的节点占用空间大 深度优先有回溯的操作(没有路走了需要回头)所以相对而言时间会长一点 深度优先采用的是堆栈的形式, 即先进后出 广度优先则采用的是队列的形式, 即先进先出

插入排序

循环遍历原始数据,把原始数组内的每一个逐个插入到新数组内 在插入的时候,按照一定的顺序插入。

function sort(arr) {//原地
 for (let i in arr) {//选一个元素
  while (i>0&&arr[i]<arr[i-1]) {//向前移动到合适的位置
   [arr[i],arr[i-1]]=[arr[i-1],arr[i]]
   i--
  }
 }
}

归并排序

归并排序其实可以类比二分法,二分法其实就是二等分的意思,就是不断和新序列的中间值进行比较。

function sort(arr) {
 if (arr.length===1) return arr

 //分成两部分
 let mid=Math.floor(arr.length/2)
 let [part1,part2]=[sort(arr.slice(0,mid)),sort(arr.slice(mid))]

 //对比+合并
 let result=[]
 while (part1.length>0&&part2.length>0)
  result.push((part1[0]<part2[0]?part1:part2).shift())
 return [...result,...part1,...part2]
}

快速排序

选择数组中的一个值作为基准,将数组中小于该值的数置于该数之前,大于该值的数置于该数之后,接着对该数前后的两个数组进行重复操作直至排序完成。

function sort(arr) {
 if (arr.length<=1) return arr
    //选基准值
 let mid_pos=arr.length>>1
 let mid=arr.splice(mid_pos,1)[0]

 let left=[],right=[]

    //和基准值比较,分别插入left,right数组
 arr.forEach(item=>(item<=mid?left:right).push(item))

 return [...sort(left),mid,...sort(right)]//递归调用排序
}

二分查找

二分查找就是将需要查找的元素不断地与数组中间的元素进行比较,数组不断地拆分为两段(查找元素小于中间值在前半段查找,大于中间值在后半段进行查找)

function search(arr,target) {//循环写法,不断移动左右指针,缩小范围
 let left=0,right=arr.length-1

 while (left<=right) {
  const mid_pos=Math.floor((left+right)/2)
  const mid_val=arr[mid_pos]

  if (target===mid_val) {
   return mid_pos
  } else if (target>mid_val) {
   left=mid_pos+1
  } else {
   right=mid_pos-1
  }
 }
 return -1
}

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情