常见的前端算法

175 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 15 天,点击查看活动详情

常见的前端算法

冒泡排序,二叉树遍历,最长回文,二分查找,指针,链表等,堆栈,队列

数据结构

逻辑结构:集合、线性、树形、图形结构
物理结构:顺序、链式存储结构

冒泡排序

冒泡排序算法的原理如下:

比较相邻的元素。如果第一个比第二个大,就交换他们两个。

对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。

针对所有的元素重复以上的步骤,除了最后一个。

持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比比较

// 编写方法,实现冒泡
var arr = [29,45,51,68,72,97];
// 外层循环,控制趟数,每一次找到一个最大值
for (var i = 0; i < arr.length - 1; i++) {
    // 内层循环,控制比较的次数,并且判断两个数的大小
    for (var j = 0; j < arr.length - 1 - i; j++) {
        // 白话解释:如果前面的数大,放到后面(当然是从小到大的冒泡排序)
        if (arr[j] > arr[j + 1]) {
            var temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
        }
    }

}
console.log(arr); // [2, 4, 5, 12, 31, 32, 45, 52, 78, 89]

数组去重

利用ES6的Set数据结构的特性

let arr = [1,1,'true','true',true,true,15,15,false,false,undefined,undefined, null,null, NaN, NaN,'NaN','NaN', 0, 0, 'a', 'a',{},{}];
function distinct(arr) {
    return Array.from(new Set(arr))
}

function distinct(arr) {
    return [...new Set(arr)]
}

console.log(distinct(arr)) // [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]

防抖和节流

防抖(debounce)

所谓防抖,就是指触发事件后n秒内函数只能执行一次,如果在n秒内再次触发了事件,则会重新计算函数执行时间。

非立即执行版

function debounce(func, wait) {
    let timeout;
    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout);
        
        timeout = setTimeout(() => {
            func.apply(context, args)
        }, wait);
    }
}

立即执行版本

function debounce(func,wait) {
    let timeout;
    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout);

        let callNow = !timeout;
        timeout = setTimeout(() => {
            timeout = null;
        }, wait)

        if (callNow) func.apply(context, args)
    }
}

双剑合璧版

/**
 * @desc 函数防抖
 * @param func 函数
 * @param wait 延迟执行毫秒数
 * @param immediate true 表立即执行,false 表非立即执行
 */
function debounce(func,wait,immediate) {
    let timeout;

    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout);
        if (immediate) {
            var callNow = !timeout;
            timeout = setTimeout(() => {
                timeout = null;
            }, wait)
            if (callNow) func.apply(context, args)
        }
        else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
    }
}

节流 (throttle)

节流,就是指连续触发事件在n秒中只执行一次函数,节流会稀释函数的执行频率。

时间戳版本

function throttle(func, wait) {
    let previous = 0;
    return function() {
        let now = Date.now();
        let context = this;
        let args = arguments;
        if (now - previous > wait) {
            func.apply(context, args);
            previous = now;
        }
    }
}

定时器版本

function throttle(func, wait) {
    let timeout;
    return function() {
        let context = this;
        let args = arguments;
        if (!timeout) {
            timeout = setTimeout(() => {
                timeout = null;
                func.apply(context, args)
            }, wait)
        }

    }
}

双剑合璧版

/**
 * @desc 函数节流
 * @param func 函数
 * @param wait 延迟执行毫秒数
 * @param type 1 表时间戳版,2 表定时器版
 */
function throttle(func, wait ,type) {
    if(type===1){
        let previous = 0;
    }else if(type===2){
        let timeout;
    }
    return function() {
        let context = this;
        let args = arguments;
        if(type===1){
            let now = Date.now();

            if (now - previous > wait) {
                func.apply(context, args);
                previous = now;
            }
        }else if(type===2){
            if (!timeout) {
                timeout = setTimeout(() => {
                    timeout = null;
                    func.apply(context, args)
                }, wait)
            }
        }
    }
}

快速排序

// 快排
function quickSort(arr, i, j) {
  if(i < j) {
    let left = i;
    let right = j;
    let pivot = arr[left];
    while(i < j) {
      while(arr[j] >= pivot && i < j) {  // 从后往前找比基准小的数
        j--;
      }
      if(i < j) {
        arr[i++] = arr[j];
      }
      while(arr[i] <= pivot && i < j) {  // 从前往后找比基准大的数
        i++;
      }
      if(i < j) {
        arr[j--] = arr[i];
      }
    }
    arr[i] = pivot;
    quickSort(arr, left, i-1);
    quickSort(arr, i+1, right);
    return arr;
  }
}

// example
let arr = [2, 10, 4, 1, 0, 9, 5 ,2];
console.log(quickSort(arr, 0 , arr.length-1));
function quickSort(arr, i, j) {
  if(i < j) {
    let left = i;
    let right = j;
    let mid = Math.floor((left+right)/2);
    let temp = arr[left];
    arr[left] = arr[mid];
    arr[mid] = temp;
    let pivot = arr[left];
    while(i < j) {
      while(arr[j] >= pivot && i < j) {  // 从后往前找比基准小的数
        j--;
      }
      if(i < j) {
        arr[i++] = arr[j];
      }
      while(arr[i] <= pivot && i < j) {  // 从前往后找比基准大的数
        i++;
      }
      if(i < j) {
        arr[j--] = arr[i];
      }
    }
    arr[i] = pivot;
    quickSort(arr, left, i-1);
    quickSort(arr, i+1, right);
    return arr;
  }
}

随机颜色

// 随机颜色,十六进制方法;
function getRandomColor( ) {
    var rand = Math.floor(Math.random( ) * 0xFFFFFF).toString(16);
    if(rand.length == 6){
        return rand;
    }else{
        return getRandomColor();
    }
}

打乱数组随机输出

var a = [1,2,3,4,5,6,7,8,9]
function randomSort(arr) {
    return arr.sort((a, b) => {
        return Math.random() > 0.5 ? 1 : -1;
    })
}

randomSort(a)

合并两个升序数组为一个升序数组

var a=[1,3,4,5,7,8,9];
var b=[3,4,5,6,7,8,9];
 
var c= a.concat(b);
console.log(c);
for(var i=0;i<c.length;i++){
	for(var j=i+1;j<c.length;j++){
		if(c[i]==c[j]){
			c.splice(j,1);
			j--;
		}
	}
}
 
console.log(c);
c.sort();
console.log(c);

输入一个链表,反转链表后,输出新链表的表头

function reverseList(pHead) {
	// 缓存传入的 pHead,创建一个空数组接收链表的值
	var node = pHead, arr = [];
	// 当节点不为空时,依次将节点值传入数组
	while(node != null) {
		arr.push(node.val);
		node = node.next;
	}
	// 上面循环结束后 node 是表尾,需要重新定义表头
	node = pHead;
	// 将数组中的值 pop 出来依次赋给节点
	while(node != null) {
		node.val = arr.pop();
		node = node.next;
	}
	return pHead;
}

反转二叉树

// 方法 一
var invertTree = function (root) {
   if (root !== null) {
     var temp = root.left;
     root.left = root.right;
     root.right = temp;
     invertTree(root.left);
     invertTree(root.right);
   }
   return root;
}

// 方法 二
var invertTree = function (root) {
   if (root !== null) {
     [root.left, root.right] = [invertTree(root.right), invertTree(root.left)]
   }
   return root;
}

统计HTML标签中以b开头的标签数量

const tags = document.getElementsByTagName('*');
// 要使用数组的方法必须将类数组转为真正的数组
const value = [...tags].filter((item) => item.tagName.startsWith('B'))

统计HTML标签中出现次数最多的标签

const tags = document.getElementsByTagName('*');
let map = new Map();
let maxStr = '';
let max = 0;
// 只是使用下标来获取,没有使用数组的方法,所以不需要将类数组转为数组
for(let i = 0; i < tags.length; i++) {
    let value = map.get(tags[i].tagName)
    if(value) {
        map.set(tags[i].tagName, ++value)
    } else {
        map.set(tags[i].tagName, 1);
    }
    if(value > max) {
        maxStr = tags[i].tagName;
        max = value;
    }
}

console.log(`当前最多的标签为 ${maxStr},个数为 ${max}` );