js常见算法

123 阅读3分钟
  1. 冒泡算法
基本思想:重复遍历要排序的数组,每次比较相邻两个元素的大小,如果顺序错误,则交换两个元素的位置

步骤:
1.比较相邻两个元素,如果位置不对交换两个元素
2.对每一对相邻的元素重读步骤1,直到最后,把最大(小)元素冒泡的最后一个位置
3.针对所有(除最后一个)元素,重复步骤1,2
重复步骤1,2,3,直到排序完成

function bubbleSort (arr) {
    let len = arr.length;
    for(let i=0; i<len; i++) {
        for(let j=0; j<len-1-i;j++) {
            if(arr[j]>arr[j+1]) {
                [arr[j],arr[j+1]] = [arr[j+1], arr[j]];
            }
	}
    }
    return arr;
}
let arr = [2, 9, 30,10, 17, 31, 28, 5];
bubbleSort(arr)

2.快速排序

基本思想:他是一种分而治之的算法,找出一个参考值,通过递归的方式将数据一次分解为包含较小元素和较大元素的
不同子序列,重复这个步骤直到所有数据都是有序的
步骤:
1.  选择一个参考元素,将数组分割为两个子序列
2.  对序列重新排序,将所有小于基准的元素放在基准值的前面,大于基准值的元素放在基准值的右侧
3.  分别对小元素的子序列和较大元素的子序列重复步骤12

function quickSort (arr) {
    let len=arr.length,left=[],right=[],current = arr[0];
    if(len<=1) return arr;
    for(let i=1; i<len; i++){
        if(arr[i]<current) {
            left.push(arr[i]);
        } else {
            right.push(arr[i])
        }	
    }
    // 递归步骤1,2
    return quickSort(left).concat(current, quickSort(right))
}

3.插入排序

基本思想:通过构建有序序列,对于未排序数据,在已排序的队列中从后向前扫描,找到相应的位置并插入。
步骤:
1.  从第一个元素开始,该元素被认为是已经排序
2.  取出下一个与元素,在已经排序的队列中从后向前扫描
3.  如果该元素(已排序)大于新元素,将元素移到下一个位置
4.  重复步骤3,直到找到已排序元素中小于或等于新元素的位置
5.  将新元素插入到这个位置
6.  重复步骤25

/**双层循环,外循环控制未排序的元素,内循环控制已排序的元素,将未排序元素设为标杆,
与已排序的元素进行比较,小于则交换位置,大于则位置不动
*/
function insertSort(arr) {
    let tem
    for(let i=0; i<arr.length; i++) {
        tem = arr[i]
        for(let j=i; j>=0; j--){
            if(arr[j-1] > tem){
                arr[j] = arr[j-1]
            } else {
                arr[j] = tem
                break
            }
        }
    }
    return arr
}

4.选择排序

基本思想:首先在待排序的序列中选出最大值(或最小值),存放在排序序列的起始位置,然后再从剩余未排序的序列
中继续选出最大值(或最小值),放在已排序的队列的末尾,以此类推,直到所有元素排序完成。
步骤:
1.  初始状态:无序区为[1,...,n], 有序区为空
2.  第i(i=1,2,3...n)趟排序时,当前有序区和无需区分别为[1, ...,i-1]和[i,...,n], 该趟排序从无序列表中选出
最小(大)的元素,将它与无需区的第一个元素交换
3.  n-1趟结束后,数组就是有序的了

/**
 * 先假设第一个元素为最小的,然后通过循环找出最小元素,
 * 然后同第一个元素交换,接着假设第二个元素,重复上述操作即可
 */
function selectSort(arr) {
    let len = arr.length, minIndex, tem
    for(let i=0; i<len-1; i++) {
        minIndex = i //最小值下标
        for(let j=i+1; j<len; j++) {
            if(arr[j] < arr[minIndex]){
                // 找出最小值
                minIndex = j //更换最小值下标
            }
        }
        // 交换位置
        tem = arr[i]
        arr[i] = arr[minIndex]
        arr[minIndex] = tem
    }
    return arr
}

5.归并排序

基本思想:将若干有序序列逐步归并,最终归并为一个有序序列。
步骤:
1.  把长度为n的序列分成两个长度为n/2的子序列
2.  对这两个子序列分别采用归并排序
3.  将排好序的子序列合并成最终的有序序列
// 将数组一直等分,然后合并
function merge(left, right) {
    let tem = [];
    while(left.length && right.length) {
        if(left[0] < right[0]) {
            tem.push(left.shift());
        }else{
            tem.push(right.shift());
        }
    }
    return tem.concat(left,right);
}
function mergeSort(arr) {
    const len = arr.length;
    if(len<2) return arr;
    let mid = Math.floor(len / 2), left = arr.slice(0,mid), right = arr.slice(mid);
    return merge(mergeSort(left),mergeSort(right));
}

6.希尔排序

基本思想:把记录按的一定增量分组,对每组使用直接插入排序,随着增量的逐步减少,每组包含的元素越来越多,当增
量减少到1时,整个序列被分成一组,排序完成。

步骤:
1.选择一个增量序列t1,t2,t3…tk, 其中ti>tj,tk=1
2.按增量序列个数k,对序列进行k趟排序
3.每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量
因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度

function shellSort(arr) {
    var len = arr.length;
    for(var gap = Math.floor(len / 2); gap > 0; gap = Math.floor(gap / 2)) {
        for(var i = gap; i < len;i++) {
            var j = i;
            var current = arr[i];
            while (j - gap >= 0 && current < arr[j - gap]) {
                 arr[j] = arr[j - gap];
                 j = j - gap;
            }
            arr[j] = current;
        }
    }
    return arr;
}
  1. 数组翻转
// [].reverse()
let arr = [2, 9, 30, 17, 31, 28, 5];
for (let i = 0; i < arr.length / 2; i++) {
  let num = arr[i];
  arr[i] = arr[arr.length -1 - i];
  arr[arr.length -1 - i] = num;
}

8.数组去重

// [...new Set(arr)]
let arr = [1,2,3,4,5,3,2,4,1];
for (let i = 0; i < arr.length; i++) {
  for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j]) {
        arr.splice(j, 1);
        j--;
      }
  }
}

9.数组交集,并集,差集

let a = [1,2,3]
let b = [4,3,2]

交集 
(1).let intersect = new Set([...a].filter(v => b.has(v))); // [2,3] 
(2).const jiaoji = (arr1, arr2) => {
  return arr1.filter(v => arr2.some(j =>  v === j ));
}
jiaoji(a,b)

并集 let union = new Set([...a, ...b]) // [1,2,3,4] 

差集 
(1).difference = new Set([...a].filter(v => !b.has(v)); // [1, 4]
(2).const chaji = (arr1, arr2) => {
  return arr1.filter(v => arr2.every(j =>  v !== j ));
}
chaji(a,b)
给定一个包括'(',')','{','}','[',']'的字符串
判断如果'()', '[]', '{}', '{{[]}}'true, '(]', '[}'..为false
const isVolid = (v) => {
  let items = [];
  let len = v.length;
  if (len % 2 !== 0) {
    return false;
  }
  for (let i = 0; i < len; i++) {
    let letter = items[items.length - 1];
    switch (v[i]) {
      case '(':
        items.push('(');
        break;
      case '{':
        items.push('{');
        break;
      case '[':
        items.push('[');
        break;
      case ')':
        if (letter === '(') {
          items.pop()
        }
        break;
      case '}':
        if (letter === '{') {
          items.pop()
        }
      break;
      case ']':
        if (letter === '[') {
          items.pop()
        }
        break;
    }
  }
  return items.length === 0;
}
  1. 斐波那契数列之和 (最后一项等于前面两项之和 0,1,1,2,3,5,8,13,21,34...)
const generateFib = (n) => {
  let arr = [];
  let i = 0;
  while (i < n) {
    if (i <= 1) {
      arr.push(i);
    } else {
      arr.push(arr[i-1] + arr[i -2])
    }
    i++;
  }
  return arr
}

12.斐波那契第n项

const fibSuper = (n) => {
  if (n < 2) return n;
  const arr = [0, 1];
  for (let i = 2; i <= n; i++) {
    arr.push(arr[0] + arr[1]);
    arr.splice(0, 1);
  }
  return arr[1]
}

13.求数组最大差值

const getMaxProfit = (arr) => {
  let maxProfit = 0;
  let minVal = arr[0];
  for (let i = 0; i < arr.length; i++) {
    const currentVal = arr[i];
    minVal = Math.min(minVal, currentVal); // 取最小值
    const temp = currentVal - minVal;
    maxProfit = Math.max(maxProfit, temp);
  }
  return maxProfit;
}

14.求数组中每个数字出现的次数

// [2,5,5,9,9,17,22,28,30,30]
const keyWordCount = (arr) => {
  return arr.reduce((pre, cur) => {
    if (!pre[cur]) {
      pre[cur] = 1
    } else {
      pre[cur]++
    }
    return pre;
  }, {})
}

节流

// 一个函数执行后,只有大于规定时间才执行第二次,只让函数第一次执行生效,后面不生效
const throttle = function(fn, delay) {
  let lastTime = 0;
  return function() {
    const nowTime = Date.now();
    if (nowTime - lastTime > delay) {
      fn.call(this);
    }
    lastTime = nowTime; // 同步时间
  }
}

防抖

// 一个需要频繁触发的函数,在规定时间内,只让最后一次执行生效,前面不生效
const debounce = function (fn, delay) {
  let timer = null;
  return function() {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn();
    }, delay || 200)
  }
}

深拷贝

function deepCopy(obj) {
  if (typeof obj === 'object' && typeof obj !== 'function') {
    return obj;
  }
  let  o = Object.prototype.toString.call(obj) === '[object Array]' ? [] : {};
  for (i in obj) {
    if (obj.hasOwnProperty(i))
      o[i] = typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i];
    }
  
  return o;
}