前端相关算法

162 阅读1分钟

数组去重

标准写法

let arr = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5]
// 数组去重
function rmSame(arr) {
  let res = []
  for (v in arr) {
    if (res.indexOf(arr[v]) == -1) res.push(arr[v])
  }
  return res
}

console.log(rmSame(arr))

set 写法

let arr = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5]
// 利用 set 数组去重
function delSame(arr) {
  let set = new Set(arr)
  return [...set] // es6 语法
}
console.log(delSame(arr))

排序算法

冒泡排序

动画演示

原理:数组之间两两比较,将小的提前,然后重复这个操作,直到所有数组元素都是从小到大排列为止。

// 冒泡排序
Array.prototype.bubble = function () {
  let arr = this
  while (true) {
    let finished = true
    for (let i = 1; i < arr.length; i++) {
      if (arr[i] < arr[i - 1]) {
        let temp = arr[i]
        // 交换
        arr[i] = arr[i - 1]
        arr[i - 1] = temp
        finished = false
      }
    }
    if (finished === true) break
  }
  return arr
}

选择排序

动画演示

原理:在未排序的数组中寻找最小的元素,将最小的元素与已经排好的元素的下一个元素进行交换。重复操作直到所有数组都排列完成。

// 选择排序
Array.prototype.select = function () {
  let arr = this
  for (let i = 0; i < arr.length; i++) {
    // 未被排序的元素中的最小值的索引
    // i 为未被排序的第一个元素的索引
    let minIndex = i
    // j 未循环遍历未被排序的元素的索引
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[j] < arr[minIndex]) {
        minIndex = j
      }
    }
    // 将未被排序中的最小元素进行排序
    let temp = arr.splice(minIndex, 1)[0]
    arr.splice(i, 0, temp)
  }
  return arr
}

插入排序

动画演示

将未排序的第一个元素和已排序的最后一个元素从后往前一次进行比较,当未排序的元素 j 小于已排序的元素 i 并且大于已排序的元素 i - 1, 此时将未排序的元素 j 插入到 i 和 i - 1 之间。

// 插入排序
Array.prototype.insert = function () {
  let arr = this
  for (let i = 1; i < arr.length; i++) {
    // mark 为将要进行插入的位置,i 未排序的第一个元素
    let mark = i
    // 倒叙遍历已经进行排序的元素
    for (let j = i - 1; j >= 0; j--) {
      if (arr[i] >= arr[j]) break
      mark = j
    }
    // 将元素插入到标记的插入位置
    let temp = arr.splice(i, 1)[0]
    arr.splice(mark, 0, temp)
  }
  return arr
}

归并排序

动画演示

将数组元素分成两份,比较两份中的第一个元素,谁小谁就弹出到一个新数组的尾部。

// 归并排序
Array.prototype.merge = function () {
  let arr = this
  // 递归的退出条件,只剩一个元素时退出递归
  if (arr.length <= 1) return arr
  let mid = Math.floor(arr.length / 2)
  let left = arr.slice(0, mid)
  let right = arr.slice(mid)
  // 对分好的 left 和 right 递归重复操作,将程序从后往前运行
  left = left.merge()
  right = right.merge()
  let res = []
  // 左右两个数组都有元素时一直执行
  while (left.length + right.length) {
    // 如果左第一个元素小于右第一个元时或当右数组为元素清空时执行 if
    // 如果左第一个元素大于于右第一个元时或左数组元素清空时执行 else
    if (left[0] <= right[0] || right.length == 0) {
      res.push(left.shift())
    } else {
      res.push(right.shift())
    }
  }
  return res
}

快速排序

动画演示

选择一个基准,将小于基准的元素放到基准左侧,大于基准的元素放到基准右侧。然后对基准两侧的元素进行递归。

// 快速排序
Array.prototype.quick = function () {
  let arr = this
  // 递归的退出条件
  if (arr.length <= 1) return arr
  // 选取最后一个元素作为基准
  let baseIndex = arr.length - 1
  let left = []
  let right = []
  // 遍历除了基准之外的元素,如果小于基准则添加在 left 如果大于基准则添加到 right
  for (let i = 0; i < baseIndex; i++) {
    if (arr[i] <= arr[baseIndex]) {
      left.push(arr[i])
    } else {
      right.push(arr[i])
    }
  }
  // 将划分好的 left 和 right 执行上面相同的操作,并返回处理好的 left 和 right
  left = left.quick()
  right = right.quick()
  // 将处理好的进行拼接
  let res = [].concat(left, arr[baseIndex], right)
  return res
}

查找算法

二分查找

先查找已排好序元素的中间值,如果中间值和需要查找值相等则输出中间值的索引。如果中间值大于查找值,那么在中间值左侧元素进行递归;如果中间值小于查找值,那么则在中间值右侧进行递归。

// 二分查找
Array.prototype.search = function (value, head, tail) {
  let arr = this
  if (!head) head = 0
  if (!tail) tail = arr.length

  let mid = Math.floor((tail + head) / 2)
  if (arr[mid] == value) return mid

  // 递归的退出条件,如果此时未查找到下一次递归 mid 和 tail 将一直不变出现死循环
  if (head + 1 == tail) return -1

  if (value < arr[mid]) {
    return arr.search(value, head, mid)
  } else {
    return arr.search(value, mid, tail)
  }
}

浅拷贝和深拷贝

浅拷贝

var obj = {
  id: 1,
  name: "andy",
  msg: {
    age: 18
  }
}
var o = {}

// 浅拷贝
function simpleCopy(newobj, oldobj) {
  for (k in oldobj) {
    newobj[k] = oldobj[k]
  }
}
simpleCopy(o, obj)
console.log(o)

深拷贝

var obj = {
  id: 1,
  name: "andy",
  msg: {
    age: 18
  }
}
var o = {}

// 深拷贝
function deepCopy(newobj, oldobj) {
  // 判断我们的属性值属于哪种数据类型
  for (var k in oldobj) {
    // 1. 获取属性值
    var item = oldobj[k]
    // 2. 判断这个值是否是数组
    if (item instanceof Array) {
      newobj[k] = []
      deepCopy(newobj[k], item)
    } else if (item instanceof Object) {
      // 3. 判断这个值是否是对象
      newobj[k] = {}
      deepCopy(newobj[k], item)
    } else {
      // 4. 属于简单数据类型
      newobj[k] = item
    }
  }
}
deepCopy(o, obj)
console.log(o)

提取需要的 json 数据

// 我们需要处理的 json 数据
const data = [
  {
    name: "brokyz",
    age: "21",
    hobby: [{ hobby1: "sleep", hobby2: "code" }]
  },
  {
    name: "simple",
    age: "40",
    hobby: [{ hobby1: "make", hobby2: "eat" }]
  }
]
// 我们需要的格式
/*
  [
    {
      name: "brokyz"
      hobby:{hobby2: "code"}
    }
  ]
*/
const newData = data.map((item) => {
  // console.log(item)
  const obj = {
    name: item.name
  }
  obj.hobby = item.hobby.map((it) => {
    return { hobby2: it.hobby2 }
  })[0]
  return obj
})
console.log(newData)

数组扁平化

let arr = [1, [[2, 3]], 4, [[[5, 6, 7, 8]]], 9]

Array.prototype.flat = function () {
  let arr = this
  const res = arr.map((item) => {
    if (item instanceof Array) {
      return item.flat()
    }
    return item
  })
  return [].concat(...res)
}

console.log(arr.flat())

防抖与节流

防抖

let input = document.querySelector("#input")
// let t = null
// input.oninput = function () {
//   if (t) clearTimeout(t)
//   t = setTimeout(() => {
//     console.log(this.value)
//   }, 500)
// }

function noShake(fn, delay) {
  let t = null
  return function () {
    if (t) clearTimeout(t)
    t = setTimeout(() => {
      fn.call(this)
    }, delay)
  }
}

input.oninput = noShake(function () {
  console.log(this.value)
}, 500)

节流

let input = document.querySelector("#input")
// 节流
let t = true
input.oninput = function () {
  if (t) {
    setTimeout(() => {
      console.log(this.value)
    }, 500)
    t = false
  }
}