时间复杂度
目的
我们通常使用最差的时间复杂度来衡量一个算法的好坏
如何达成
在计算时间复杂度的时候,数据量通常是非常大的,所以我们忽略低阶项和常数项,计算出操作次数为 aN + 1
,N
代表数据量。那么该算法的时间复杂度就是 O(N)
条件
- 常数时间
O(1)
代表这个操作和数据量没关系,是一个固定时间的操作,比如说四则运算 - 计算出操作次数为
aN + 1
,N
代表数据量。那么该算法的时间复杂度就是O(N)
- 当出现两个算法都是
O(N)
的时间复杂度,那么对比两个算法的好坏就要通过对比低阶项和常数项了
栈
是什么
栈是一个线性结构,在计算机中是一个相当常见的数据结构
分类边界
只能在某一端添加或删除数据,遵循先进后出的原则
如何达成
每种数据结构都可以用很多种方式来实现,其实可以把栈看成是数组的一个子集,所以这里使用数组来实现
class Stack {
// 构造器
constructor() {
this.stack = []
}
// 入栈
push(item) {
this.stack.push(item)
}
// 出栈
pop() {
this.stack.pop()
}
// 栈内元素个数
getCount() {
return this.stack.length
}
// 判断栈是否为空
isEmpty() {
return this.getCount() === 0
}
// 栈顶元素的值
peek() {
return this.stack[this.getCount() - 1]
}
}
条件
题意是匹配括号,可以通过栈的特性来完成这道题目
var isValid = function (s) {
let map = {
'(': -1,
')': 1,
'[': -2,
']': 2,
'{': -3,
'}': 3
}
let stack = []
for (let i = 0; i < s.length; i++) {
if (map[s[i]] < 0) {
stack.push(s[i])
} else {
let last = stack.pop()
// 注意这里是[]
if (map[last] + map[s[i]] != 0) return false
}
}
if (stack.length > 0) return false
return true
};
在 Vue 中关于模板解析的代码,就有应用到匹配尖括号的内容
队列
是什么
队列是一个线性结构
分类边界
在某一端添加数据,在另一端删除数据,遵循先进先出的原则
组合
单链队列 O(n)
class Queue {
constructor() {
this.queue = []
}
// 入队
enQueue(item) {
this.queue.push(item)
}
// 出队
deQueue() {
return this.queue.shift()
}
// 队首元素
getHeader() {
return this.queue[0]
}
// 队中元素个数
getLength() {
return this.queue.length
}
// 队列是否为空
isEmpty() {
return this.getLength() === 0
}
}
因为单链队列在出队操作的时候需要 O(n)
的时间复杂度,所以引入了循环队列。循环队列的出队操作平均是 O(1)
的时间复杂度
循环队列 O(1)
class SqQueue {
constructor(length) {
this.queue = new Array(length + 1)
// 队头
this.first = 0
// 队尾
this.last = 0
// 当前队列大小
this.size = 0
}
enQueue(item) {
// 判断队尾 + 1 是否为队头
// 如果是就代表需要扩容数组
// % this.queue.length 是为了防止数组越界
if (this.first === (this.last + 1) % this.queue.length) {
this.resize(this.getLength() * 2 + 1)
}
this.queue[this.last] = item
this.size++
this.last = (this.last + 1) % this.queue.length
}
deQueue() {
if (this.isEmpty()) {
throw Error('Queue is empty')
}
let r = this.queue[this.first]
this.queue[this.first] = null
this.first = (this.first + 1) % this.queue.length
this.size--
// 判断当前队列大小是否过小
// 为了保证不浪费空间,在队列空间等于总长度四分之一时
// 且不为 2 时缩小总长度为当前的一半
if (this.size === this.getLength() / 4 && this.getLength() / 2 !== 0) {
this.resize(this.getLength() / 2)
}
return r
}
getHeader() {
if (this.isEmpty()) {
throw Error('Queue is empty')
}
return this.queue[this.first]
}
getLength() {
return this.queue.length - 1
}
isEmpty() {
return this.first === this.last
}
resize(length) {
let q = new Array(length)
for (let i = 0; i < length; i++) {
q[i] = this.queue[(i + this.first) % this.queue.length]
}
this.queue = q
this.first = 0
this.last = this.size
}
}
链表
是什么
- 链表是一个线性结构,同时也是一个天然的递归结构
- 链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理
- 但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大
分类边界
组合
单向链表
// 定义节点
class Node {
constructor(v, next) {