时间复杂度
常见的时间复杂度
执行函数
阶
术语
12
O(1)
常数阶
2n+3
O(n)
线性阶
3n2 + 2n + 1
O(n2)
平方阶
O(logn)
对数阶 (logN底数默认为2)
O(nlogn)
nlogn阶
O(n3)
立方阶
O(2n)
指数阶
常用的时间复杂度耗时排序: 常数阶 < 对数阶 < 线性阶 < nlogn阶 < 平方阶 < 立方阶 < 指数阶
冒泡排序
每次比较相邻2个元素,如果顺序错误就交换位置。O(n2),不稳定
function bubbleSort(arr) {
if (arr.length <= 1) {
return arr
}
var len = arr.length
for (var i = 0; i <= len - 1; i++) {
for (var j = 0; j <= len -1; j++) {
if (arr[j] > arr[j + 1]) {
// var temp
// temp = arr[j]
// arr[j] = arr[j + 1]
// arr[j + 1] = temp
// 不通过临时变量实现变量交换
arr[j + 1] = [arr[j], arr[j] = arr[j + 1]][0]
}
}
}
return arr
}选择排序
首先找到未排序最小元素放到首位,再寻找剩余的最小往后排。O(n2),稳定
function selectionSort (arr) {
if (arr.length <= 1) {
return arr
}
for (var i = 0; i < arr.length - 1; i++) {
var minIndex = i;
for (var j = i + 1; j < arr.length; j++) {
if (arr[minIndex] > arr[j]) {
minIndex = j;
}
}
arr[i] = [arr[minIndex], arr[minIndex] = arr[i]][0]
}
return arr
}快速排序
取中间参照元素,小的放左数组,大的放右数组,再使用该方法继续排序。O(nlogn),不稳定
function quickSort(arr) {
if (arr.length <= 1) {
return arr
}
// 计算参照位
var cIndex = Math.floor(arr.length / 2)
var c = arr.splice(cIndex, 1)
// 注意 Number([1]) === 1
var l = []
var r = []
for (var i = 0; i < arr.length; i++) {
if (arr[i] < c) {
l.push(arr[i])
} else {
r.push(arr[i])
}
}
return quickSort(l).concat(c, quickSort(r))
}插入排序
在已排序的序列中从后向前扫描,找到相应位置并插入。 O(n2),稳定
function insertionSort (arr) {
// 第一个数肯定是有序的,从第2个数开始遍历,依次插入
for (var i = 1; i < arr.length; i++) {
var temp = arr[i]
var j = i - 1 // i - 1 个数肯定是有序的
while (j >= 0 && arr[j] > temp) {
arr[j + 1] = arr[j]
j--
}
arr[j + 1] = temp
}
return arr
}二分搜索法
也称折半搜索,是一种在有序数组中查找特定元素的搜索算法。
首先从数组中间开始查找对比,若相等则找到,直接返回中间元素的索引。
若查找值小于中间值,则在小于中间值的那一部分执行步骤1的操作。
若查找值大于中间值,则在大于中间值的那一部分执行步骤1的操作。
否则,返回结果为查不到,返回-1。
// 递归实现
function binarySeach(target, arr, start, end) {
var start = start || 0
var end = end || arr.length - 1
var mid = parseInt(start + (end - start) / 2)
// 排除查找对象在数组之外
if (target > arr[end] || target < arr[start]) {
return -1
} else if (target === arr[mid]) {
return true
} else if (target > arr[mid]) {
return binarySeach(target, arr, mid + 1, end)
} else {
return binarySeach(target, arr, start, mid - 1)
}
}
// 不使用递归
function binarySeach(target, arr) {
var start = 0
var end = arr.length - 1
// 排除查找对象在数组之外
if (target > arr[end] || target < arr[start]) {
return -1
}
while (start <= end) {
var mid = parseInt(start + (end - start) / 2)
if (target === arr[mid]) {
return mid
} else if (target > arr[mid]) {
start = mid + 1
} else {
end = mid - 1
}
}
}无序的二分搜索
function binarySeach(target, arr) {
while (arr.length > 0) {
// 快速排序
var left = []
var right = []
var pivot = arr[0]
for (var i = 1; i < arr.length; i++) {
var item = arr[i]
item > pivot ? right.push(item) : left.push(item)
}
if (target === pivot) {
return true
} else if (target > pivot) {
arr = right
} else {
arr = left
}
}
}二叉树
二叉树的常用遍历为 前序遍历、中序遍历、后序遍历
function Node (data, left, right) {
this.data = data
this.left = left
this.right = right
}
function Tree () {
this.root = null
}
/*
1
2 3
4 5 6 7
*/
Tree.prototype = {
// 创建二叉树
create: function () {
var left = new Node(2, new Node(4), new Node(5))
var right = new Node(3, new Node(6), new Node(7))
this.root = new Node(1, left, right)
},
// 前序遍历
front: function (root) {
if (root) {
console.log(root.data)
this.front(root.left)
this.front(root.right)
}
},
// 中序遍历
middle: function (root) {
if (root) {
this.middle(root.left)
console.log(root.data)
this.middle(root.right)
}
},
// 后序遍历
backend: function (root) {
if (root) {
this.backend(root.left)
this.backend(root.right)
console.log(root.data)
}
}
}
var tree = new Tree()
tree.create()
tree.front(tree.root)
tree.middle(tree.root)
tree.backend(tree.root)数据源
[{
"name": "1",
"children": [{
"name": "1-1",
"children": [{
"name": "1-1-1"
}, {
"name": "1-1-2"
}]
}, {
"name": "1-2",
"children": [{
"name": "1-2-1"
}]
}]
}, {
"name": "2"
}, {
"name": "3",
"children": [{
"name": "3-1"
}, {
"name": "3-2",
"children": [{
"name": "3-2-1"
}, {
"name": "3-2-2"
}]
}]
}]广度优先遍历

function doBFS(dataSource) {
let stack = [...dataSource]
let item
while (stack.length) {
item = stack.shift()
console.log(item.name)
if (item.children && item.children.length) {
stack = stack.concat(item.children)
}
}
}
// 打印
1
2
3
1-1
1-2
3-1
3-2
1-1-1
1-1-2
1-2-1
3-2-1
3-2-2深度优先遍历

function doDFS(dataSource) {
let stack = [...dataSource]
let item
while (stack.length) {
item = stack.shift()
console.log(item.name)
if (item.children && item.children.length) {
stack = item.children.concat(stack)
}
}
}
// 打印
1
1-1
1-1-1
1-1-2
1-2
1-2-1
2
3
3-1
3-2
3-2-1
3-2-2递归遍历
function loop(source) {
const dataSource = JSON.parse(JSON.stringify(source))
let stack = [...dataSource]
for (let i = 0, len = dataSource.length; i < len; i++) {
console.log(dataSource[i].name)
const children = dataSource[i].children
if (children && children.length > 0) {
loop(children)
}
}
}
// 打印,同上深度优先遍历
1
1-1
1-1-1
1-1-2
1-2
1-2-1
2
3
3-1
3-2
3-2-1
3-2-2动态规划
把原问题分解成子问题进行求解。
// 计算斐波那契数列(每一项都等于前两项之和)
function fib (n) {
let last = 1
let nextLast = 1
let result = 1
for (let i = 2; i < n; i++) {
result = last + nextLast
nextLast = last
last = result
}
return result
}
// 硬币找零最大子序列之和
function getMaxSeq (arr = []) {
let max = arr[0], sum = arr[0];
for (let i = 1, len = arr.length; i < len; i++) {
sum += arr[i]
max = Math.max(sum, max)
if ( sum < arr[i]) {
sum = arr[i]
}
}
return max
}最长公共子串
function getMaxStr(str1, str2){
if (str1.length > str2.length) {
str1 = [str2, str2 = str1][0]
}
const maxLen = str1.length
for (let j = maxLen; j > 0; j--) {
for (let i = 0; i < maxLen - j; i++) {
const str = str1.substr(i, j)
if (str2.indexOf(str) !== -1) {
return str
}
}
}
return ''
}最长公共子序列
function lcs (str1, str2) {
const len1 = str1.length
const len2 = str2.length
let dp = []
for (let i = 0; i <= len1; i++) {
dp[i] = []
for (let j = 0; j <= len2; j++) {
if(i == 0 || j == 0) {
dp[i][j] = 0
continue
}
if (str1[i - 1] == str2[j - 1]) {
dp[i][j] = dp[i-1][j-1] + 1
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j -1])
}
}
}
// 最长公共子序列的长度
const maxLen = dp[len1][len2] // 返回二维数组最后一个值
const maxStr = getStr(dp, str1, str2, len1, len2)
return { maxLen, maxStr }
}
// 最长公共子序列
function getStr (dp, str1, str2, i, j) {
if (i == 0 || j == 0) {
return ""
}
if (str1[i - 1] == str2[j - 1]) {
return printLCS(dp, str1, str2, i - 1, j - 1) + str1[i - 1]
} else if (dp[i][j - 1] > dp[i - 1][j]) {
return printLCS(dp, str1, str2, i, j - 1)
} else {
return printLCS(dp, str1, str2, i - 1, j)
}
}背包问题
物品个数n=5,物品重量weights=[2,2,6,5,4],物品价值values=[6,3,5,4,6],背包总容量W=10
/**
*背包问题算法及个人理解
*
* @param {*} weightLimit 背包重量限制
* @param {*} weightArray 物品重量组成的数组
* @param {*} valueArray 物品价值组成的数组。注意顺序要与重量数组对应
* @param {*} account 物品种类(number类型)
*/
function pack (weightLimit, weightArray, valueArray, account) {
let dp = [] // 矩阵
for (let i = 0; i <= account; i++) {
dp[i] = []
for (let j = 0; j <= weightLimit; j++) {
if(i == 0 || j == 0) {
dp[i][j] = 0
continue
}
if (weightArray[i - 1] > j) {
dp[i][j] = dp[i - 1][j]
} else {
let a = valueArray[i - 1] + dp[i - 1][j - weightArray[i - 1]];
dp[i][j] = Math.max(a, dp[i - 1][j]);
}
}
}
return dp[account][weightLimit]
}双向链表
function Link(data, pre, next) {
this.data = data
this.pre = pre // 指向前一个指针
if (this.pre) {
pre.next = this
}
this.next = next // 指向后一个指针
}
Link.prototype.insert = function (node) {
if (this.next && this.next.pre) {
this.next.pre = node // 原来的后续节点的前一个变为新的
}
node.next = this.next // 新节点的下一个为原来的下一个
node.pre = this // 原来的上一个为当前此节点
this.next = node // 当前的下一个为新节点
}
Link.prototype.remove = function () {
this.next.pre = this.pre
this.pre.next = this.next
}
Link.prototype.console = function() {
if (this.next) {
return this.data + this.next.console
} else {
return this.data
}
}
// 反转
Link.prototype.reverse = function () {
var tmp = null
var self = this
function _reverse () {
if (!this.next) {
this.pre = null
this.next = tmp
return this
} else {
this.pre = this.next
this.next = tmp
tmp = this
return _reverse.call(this.pre)
}
}
return _reverse.call(this)
}Last updated
Was this helpful?