基础定义
数据结构与算法区别
- 数据结构:一组数据的存储结构;
- 算法:操作数据的一组方法;
时间复杂度
- 表示算法的执行时间与数据规模之间的增长关系;
- 只关注循环执行次数最多的代码,这段代码执行次数 n 的量级;
- 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积;
常见时间复杂度
- 多项式量级:O(1), O(logn), O(n), O(nlogn), O(n^k)
- 非多项式量级:O(2^n)、O(n!)
空间复杂度
- 表示算法的存储空间与数据规模之间的增长关系;
常见空间复杂度
4种时间复杂度
- 最好情况时间复杂度:代码在最好的情况下,循环部分执行次数;
- 最差情况时间复杂度:代码在最坏的情况下,循环部分执行次数;
- 平均时间复杂度:又称加权平均时间复杂度,各种情况求和概率*执行次数;
- 均摊时间复杂度:大部分情况下时间复杂度相同,特殊情况不同,所以把特殊情况均摊到大部分情况,即以大部分情况为准;
数据结构
数组
- 定义:数组是一种线性表数据结构,它用一组连续的内存空间,来存储一组相同类型的数据;
- 随机访问实现,一维数组内存寻址公式:a[i]_address = base_address + i * data_type_size
- 二维数组的内存寻址公式:
a[i][j] = base_address + (i*n+j)*data_type_size
- javascript中Array实际上是一种高级数组结构,将很多数组操作的细节封装起来,如数组插入、移除、越界等判断;
function myPop() {
let arr = this
let n = arr.length
if (n < 1) {
return
}
let temp = arr[n-1]
arr.length = n - 1
return temp
}
Array.prototype.myPop = myPop
function mySplice(index, num, ...add) {
let arr = this
let n = arr.length
if (n < 1) {
return []
}
if (index > n - 1 || index + num > n) {
return []
}
let del = []
let before = []
let after = []
for (let i = 0; i < n; i++) {
if (i < index) {
before.push(arr[i])
} else if (i < index + num) {
del.push(arr[i])
} else {
after.push(arr[i])
}
}
let newArr = [...before, ...add, ...after]
arr.length = newArr.length
for (let i = index; i < newArr.length; i++) {
arr[i] = newArr[i]
}
return del
}
Array.prototype.mySplice = mySplice
链表
- 定义:通过一组零散的内存块串联使用;
- 常见类型:单链表、双链表、循环链表;
- 特点:链表更适合插入、删除操作频繁的场景,查询的时间复杂度较高;
- 链表代码技巧:
- 理解指针或引用含义;
- 警惕指针丢失或内存泄漏;(先指向尾部,再连接)
- 利用哨兵简化实现难度;
- 留意边界条件处理;(null, 1个,2个,头尾等情况)
- 举例画图;
class Node {
constructor (val) {
this.val = val
this.next = null
}
}
class LinkedList {
constructor () {
this.length = 0
this.head = null
}
push (val) {
let node = new Node(val)
if (this.length === 0) {
this.head = node
this.length++
return
}
let current = this.head
while (current.next) {
current = current.next
}
current.next = node
this.length++
}
pop () {
if (this.length === 0) {
return null
}
let current = this.head
let before = null
while (current.next) {
before = current
current = current.next
}
before.next = null
this.length--
return current
}
unshift (val) {
let node = new Node(val)
if (this.length === 0) {
this.head = node
this.length++
return this.length
}
let current = this.head
node.next = current
this.head = node
this.length++
return this.length
}
shift () {
if (this.length === 0) {
return null
}
let current = this.head
this.head = current.next
this.length--
return current
}
insert (position, val) {
if (position < 0 || this.position > this.length) {
return this.length
}
let node = new Node(val)
let current = this.head
let before = null
let i = 0
while (i < position) {
before = current
current = current.next
i++
}
node.next = current
before.next = node
this.length++
return this.length
}
remove (position) {
if (this.length === 0) {
return null
}
if (position < 0 || position > this.length - 1) {
return null
}
let current = this.head
if (position === 0) {
this.head = current.next
this.length--
return current
}
let before = null
let i = 0
while (i < position) {
before = current
current = current.next
i++
}
before.next = current.next
this.length--
return current
}
}
栈
- 定义:栈是一种操作受限的线性表,只允许在一端插入和删除操作;
- 特点:先进后出,后进先出;
- 类型:顺序栈,链式栈;
- 入栈、出栈的时间复杂度都为O(1);
- 常见应用场景:浏览器路由、函数调用栈、表达式求值、括号匹配;
class router {
constructor () {
this.url = ''
this.go = []
this.back = []
}
add (url) {
this.url = url
this.go.push(url)
return this.url
}
foward () {
if (this.back.length === 0) {
return
}
let url = this.back.pop()
this.go.push(url)
this.url = url
return this.url
}
backaway () {
if (this.go.length === 0) {
return
}
const url = this.go.pop()
this.back.push(url)
this.url = this.go[this.go.length-1]
return this.url
}
}
function getValue (exp) {
let numStack = []
let calStack = []
const o = ['+', '-', '*', '/']
const n = exp.length
let str = ''
let i = 0
while (i < n) {
if (o.includes(exp[i])) {
addCal(Number(str), exp[i])
str = ''
} else {
str += exp[i]
}
i++
}
if (calStack.length) {
let result = cal(numStack[0], Number(str), calStack[0])
numStack[0] = result
} else {
numStack.push(Number(str))
}
return numStack[0]
function addCal (num, exp) {
if (calStack.length === 0) {
numStack.push(num)
calStack.push(exp)
return
}
let expLevel = getLevel(exp)
let lastLevel = getLevel(calStack[calStack.length - 1])
if (lastLevel >= expLevel) {
let result = cal(numStack[0], num, calStack[0])
numStack.push(result)
calStack.pop()
calStack.push(exp)
} else {
numStack.push(num)
calStack.push(exp)
}
}
function getLevel (exp) {
let level = {
'+': 0,
'-': 0,
'*': 1,
'/': 1
}
return level[exp]
}
function cal (num1, num2, exp) {
return eval(`${num1}${exp}${num2}`)
}
}
队列
- 定义:先进先出的一种数据结构;
- 只支持两种操作:入队和出队;
- 类型:顺序队列,链式队列,循环队列,阻塞队列,并发队列;
- 应用场景:线程池,数据库连接池,消息队列
class CircularQueue {
constructor (length) {
this.list = []
this.length = length
this.head = 0
this.tail = 0
}
enqueue (item) {
if ((this.tail + 1) % this.length === this.head) {
return false
}
this.list[this.tail] = item
this.tail = (this.tail + 1) % this.length
return true
}
dequeue () {
if (this.tail === this.head) {
return null
}
const result = this.list[this.head]
this.head = (this.head + 1) % this.length
return result
}
}
如何学习
- 边学边练,适度刷题;
- 多问、多思考、多互动;
- 考查优先级:二分法、哈希表、二叉查找树、动态规划、分治法、堆、贪心、最小生成树、字典树、并查集等;