JavaScript算法
一、算法复杂度
给定一个或多个已知值,通过一系列的运算输出所需结果的过程,叫做算法。一个需求可能可以通过多种算法来实现,学习算法让我们了解怎样的算法效率更高更快,从而让我们针对某个需求做出最合适的选择。作为一名前端攻城狮,算法可能目前离你很远,但是如果你想在编程的路上走的更远一些,或涉猎到更多的数据逻辑,算法对你的帮助很大。所以想成为一名高端的开发者,算法是必须要学习的。
讨论一个算法的好坏,有最主要的两个方面:该算法所需时间-时间复杂度,该算法占用空间(内存)-空间复杂度。耗时短占用低肯定是最优选择,而往往我们需要在两者中找到一个平衡点。作为一名程序开发者,大多数情况下,时间复杂度比空间复杂度的影响更大一些,能做的改良和优化也更多,所以一般如果不做特别说明,我们在讨论算法复杂度的时候都是指时间复杂度。
1. 时间复杂度
一个算法所需要的时间需要具体上机运行才知道,很显然不可能每个都验证一遍再做选择。我们可以对算法耗时进行合理的预测。
一个算法中语句的执行次数我们叫做时间频度,记做T(n),n表示问题规模,n增大T(n)也随之增大。而我们预估算法的效率,就是对T(n)的变化趋势进行分析,为了方便分析,我们需要简化一下T(n)函数,所以我们引入时间复杂度的概念:若有某个辅助函数 f(n),使得当n趋近与无穷大时,T(n)/f(n) 的极限值为不等于0的常数,则称 f(n) 是 T(n)的同数量级函数,记做 T(n) = O( f(n) )。O( f(n) )就是时间复杂度的表示法。
总结一下:用O( f(n) )来表示算法时间复杂度。
2. 复杂度计算举例
理论上,我们对 O( f(n) ) 中的 f(n) 没有写法限制,但是因为我们关注的是随 n 增大复杂度的变化趋势,所以各常数项和系数我们是直接省略不做考虑的。
let a = 1; let b = 2; console.log(a+b); //O(1) let z = 0; for(let i=0;i
二、js中常用算法
1.数组整数字去重
//基础写法 function fn(arr){ let newArr = [...arr]; for(let i=newArr.length-1;i>=0;i--){ for(let j=i-1;j>=0;j--){ if( newArr[i] === newArr[j] ){ newArr.splice(j,1); i --; } } } return newArr; } //O(n^2) //优化算法复杂度 function fn(arr){ let tmp = {}; let newArr = []; for(let i=0,length=arr.length;i
注意:Set结构帮助我们实现去重,不代表复杂度为 1 ,因为内部对数组已经进行了遍历,相当于做了封装。
2. 判断回文
数据结构课程中使用栈结构实现了回文,此处我们讨论一下复杂度。
3. 寻找重复最多字符
let str = "abfsafsafghjiq15445454"; function fn(str){ let length = str.length; let tmp = {}; for(let i=0;imax ){ k = [key]; max = value; }else if( value === max ){ k.push(key); } } return k; } console.log(fn(str)); //O(n)
三、排序算法
1. 冒泡排序
动图演示(动图来源visualgo (opens new window)):
function bubbleSort (arr) { let len = arr.length; //要进行多少轮排序 for(let i = 0; i < len-1; i++) { //每一轮,相邻两位进行比较,大的放后面 for(let j = 0; j < len-1-i ; j++) { if( arr[j] > arr[j+1] ){ swap(arr,j,j+1); } } } } function swap(arr,i,j){ let tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
2. 选择排序
动图演示(动图来源visualgo (opens new window)):
function selectionSort(arr){ let len = arr.length; let minIndex; for(let i = 0; i < len ; i++) { minIndex = i; for(let j = i+1; j < len ; j++) { if( arr[j] < arr[minIndex] ){ minIndex = j; } } swap(arr,i,minIndex); } } function swap(arr,i,j){ let tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
3. 插入排序
动图演示(动图来源visualgo (opens new window)):
function insertSort (arr) { let len = arr.length; let j; let current; for(let i = 1; i < len; i++) { j = i - 1; current = arr[i]; while(j >= 0 && arr[j] > current) { arr[j + 1] = arr[j]; j--; } arr[j + 1] = current; } }
4. 快速排序(分治思想)
快速排序是使用分治思想的典型代表。选择一个基准元素,把数组分成左右两部分,左边比基准小,右边比基准大,分别对左右部分递归调用快速排序。
function quickSort(arr){ if(arr.length <= 1){ return arr; } let pivot = arr[0]; let left = []; let right = []; for(let i=1;i
平均时间复杂度 O(n log n),最坏情况 O(n²),空间复杂度 O(log n)。
5. 归并排序(分治思想)
归并排序也是分治思想,通过递归分割数组并合并排序好的子数组。
function mergeSort(arr){ if(arr.length <= 1) return arr; let mid = Math.floor(arr.length / 2); let left = arr.slice(0, mid); let right = arr.slice(mid); return merge(mergeSort(left), mergeSort(right)); } function merge(left, right){ let result = []; let i=0, j=0; while(i < left.length && j < right.length){ if(left[i] <= right[j]){ result.push(left[i++]); }else{ result.push(right[j++]); } } return result.concat(left.slice(i)).concat(right.slice(j)); }
时间复杂度稳定为 O(n log n),空间复杂度为 O(n)。
四、总结
- 算法是为了解决问题而设计的计算步骤;
- 时间复杂度表示算法执行时间随问题规模变化的趋势;
- 常见时间复杂度有 O(1)、O(n)、O(n²)、O(log n)、O(n log n)、O(2^n)等;
- 数组去重、字符串处理、排序等是JS中常用的基础算法;
- 排序算法多样,冒泡、选择、插入、快速、归并各有优缺点,选择时根据实际需求。
评论(0)
暂无评论