通常状况下,算法中基本操做重复执行的次数是问题规模n的某个函数,用T(n)表示,如有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记做T(n)=O(f(n)),称O(f(n))为算法的渐进时间复杂度(O是数量级的符号 ),简称时间复杂度。git
当问题规模随着处理数据的增加时,基本操做要重复执行的次数一定会随着增加,那么咱们须要知道执行次数是以什么样的数量级增加的。github
接下来咱们用大O表示法标识一下常见的时间复杂度量级:算法
常数阶的复杂度,这种复杂度不管数据规模n如何增加,计算时间是不变的。数组
一个简单的例子:函数
const add = (a, b) => a + b
复制代码
无论n如何增加,都不会影响到这个函数的执行时间,所以这个函数的时间复杂度为O(1)。ui
线性复杂度,随着数据规模n的增加,时间复杂度也会随着n线性增加spa
const indexOf = (arr, target) => {
let len = arr.length
while(len--) {
if (arr[len] === target) {
return arr[len]
}
}
}
复制代码
对数复杂度,随着问题规模n的增加,计算时间也会随着n对数级数增加。 如数据增大1024倍时,时间只增大32倍,是比线性复杂度还要低的时间复杂度。code
典型的例子就是二分查找法。(二分查找只支持已经排好序的数组)排序
functions binarySearch(arr, target) {
let max = arr.length - 1
let min = 0
while (min <= max) {
let mid = Math.floor((max + min) / 2)
if (target < arr[mid]) {
max = mid - 1
} else if (target > arr[mid]) {
min = mid + 1
} else {
return mid
}
}
return -1
}
复制代码
在二分查找法的代码中,经过while循环,成 2 倍数的缩减搜索范围,也就是说须要通过 log2^n 次便可跳出循环。递归
事实上在实际项目中,O(logn)是一个很是好的时间复杂度,好比当n=100的数据规模时,二分查找只须要7次,线性查找须要100次,这对于计算机而言差距不大,可是当有10亿的数据规模的时候,二分查找依然只须要30次,而线性查找须要惊人的10亿次,O(logn)时间复杂度的算法随着数据规模的增大,它的优点就越明显。
平方级复杂度,典型状况是当存在双重循环的时候,即把 O(n) 的代码再嵌套循环一遍,它的时间复杂度就是 O(n²) 了,表明应用是冒泡排序算法。
冒泡排序
实现原理: 以最终目标为升序排列为例:
// 冒泡排序
function bubbleSort(arr) {
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
}
}
}
return arr
}
复制代码
冒泡排序加强版
为了提高效率,增长最小值,和最大值,而且正反分别对比一次。减小执行的对比次数,从而达到提升效率的目的。
function bubbleSortEnhancement(arr) {
let low = 0
let high = arr.length - 1
let i
let temp
while (low < high) {
for (i = low; i < high; i++) {
if (arr[i] > arr[i + 1]) {
temp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = temp
}
}
high--
for (i = high; i > low; i--) {
if (arr[i] < arr[i - 1]) {
temp = arr[i]
arr[i] = arr[i - 1]
arr[i - 1] = temp
}
}
low++
}
return arr
}
复制代码
当数据增大n倍时,执行时间随着增大nlogn倍,这个复杂度高于线性复杂度,低于平方复杂度。归并排序和快速排序就是典型的表明。
快速排序
实现原理:
// 快速排序
function quickSort(arr) {
if (arr.length <= 1) {
return arr
}
var pivotIndex = Math.floor(arr.length / 2)
var pivot = arr.splice(pivotIndex, 1)[0]
var left = []
var right = []
for (var i = 0; i < arr.length; i++) {
if (arr[i] <= pivot) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return quickSort(left).concat([pivot], quickSort(right))
}
复制代码
归并排序
实现原理:
function mergeSort(arr) {
//采用自上而下的递归方法
var len = arr.length
if (len < 2) {
return arr
}
var middle = Math.floor(len / 2),
left = arr.slice(0, middle),
right = arr.slice(middle)
return merge(mergeSort(left), mergeSort(right))
}
function merge(left, right) {
var result = []
console.time('归并排序耗时')
while (left.length && right.length) {
if (left[0] <= right[0]) {
result.push(left.shift())
} else {
result.push(right.shift())
}
}
while (left.length) result.push(left.shift())
while (right.length) result.push(right.shift())
console.timeEnd('归并排序耗时')
return result
}
复制代码