一、数据结构和算法简介
数据结构是什么: 计算机中存储数据、组织数据的结构
算法是什么: 一系列解决问题的清晰指令
说人话就是, 数据是食材, 数据结构是锅碗瓢盆, 算法是食谱。
example:
现实中: 你购买番茄和鸡蛋, 计算机中: 你获得数据。
番茄和鸡蛋有了, 需要工具来存放, 锅碗瓢盆出现了. 数据有了,需要工具进行存储和组织,数据结构出现了。
具有食材和锅碗瓢盆还不够,还需要对应的方法来烹饪。食谱出现了. 具有数据和数据结构还不够,还需要特定的方式对他们进行编码.算法出现了。
二、时间复杂度和空间复杂度
2.1 时间复杂度
时间复杂度是什么: 定型描述该算法运行时间
常见度时间复杂度:
O(1)
: Constant Complexity: Constant 常数复杂度O(log n)
: Logarithmic Complexity: 对数复杂度O(n)
: Linear Complexity: 线性时间复杂度O(n^2)
: N square Complexity 平⽅方O(n^3)
: N square Complexity ⽴立⽅方O(2^n)
: Exponential Growth 指数O(n!)
: Factorial 阶乘
代码详解:
O(1)
let i = 0
i += 1
console.log(i)
上述代码时间复杂度为 O(1).
why: console.log只执行一次,所以时间复杂度为 O(1)
O(n)
for(let i = 0; i < n; i++) {
console.log(i)
}
上述代码时间复杂度为 O(n).
why: console.log执行了n次,打印出n个值,所以时间复杂度为 O(n)
O(n^2)
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
console.log(i, j);
}
}
上述代码时间复杂度为 O(n^2).
why: console.log执行了n^2次,打印出n^2个值,所以时间复杂度为 O(n^2)
2.2 空间复杂度
一个程序的空间复杂度是指运行完一个程序所需内存的大小。说人话: 程序运行要花费多少内存.
常见度空间复杂度:
O(1)
: Constant Complexity: Constant 常数复杂度O(n)
: Linear Complexity: 线性时间复杂度O(n^2)
: N square Complexity 平⽅方
O(1)
let i = 0
i += 1
上述代码空间复杂度为 O(1).
why: 只具有一个变量i,空间复杂度为 O(1)
O(n)
let list = []
for(let i = 0; i < n; i++) {
list.push(i)
}
上述代码空间复杂度为 O(n).
why: 声明了一个list的数组,给数组中添加了n个值,空间复杂度为 O(n)
O(n^2)
let matrix = []
for (let i = 0; i < n; i++) {
matrix.push([])
for (let j = 0; j < n; j++) {
matrix[i].push(j)
}
}
上述代码空间复杂度为 O(n^2).
why: 声明了一个matrix的数组,给数组中添加了n^2个值,空间复杂度为 O(n^2).
ps: matrix为一个二维数组,空间复杂度为O(n^2), 例推 matrix为一个三维数组,空间复杂度为O(n^3)
三、数据结构之栈
3.1 栈简介
什么是栈? 栈是一种后进先出的数据结构.
let stack = []
stack.push(1) // 入栈
stack.push(2) // 入栈
const item1 = stack.pop() // 出栈
const item2 = stack.pop() // 出栈
3.2 什么场景需要用栈?
需要用到后进先出的场景都会用到栈
常见的场景:
- 十进制转二进制
- 有效的括号
- 函数调用堆栈
3.2 leetcode
20. 有效的括号
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。 示例 1:
输入:s = "()"
输出:true
示例 2:
输入:s = "()[]{}"
输出:true
示例 3:
输入:s = "(]"
输出:false
示例 4:
输入:s = "([)]"
输出:false
示例 5:
输入:s = "{[]}"
输出:true
var isValid = function(s) {
let stack = []
for(let i = 0; i < s.length; i++) {
const c = s[i]
if(s[i] === '(' || s[i] === '{' || s[i] === '[') {
stack.push(c)
} else {
const f = stack[stack.length - 1]
if( (c === '}' && f === '{') ||(c === ')' && f === '(') ||(c === ']' && f === '[') ){
stack.pop()
} else {
return false
}
}
}
return stack.length === 0 ? true : false
};
四、数据结构之队列
4.1 队列简介
什么是队列? 队列是一种先进先出的数据结构.
生活中队列? 排队买票,排队吃饭等等.
javascript代码演示
const queue = []
queue.push(1)
queue.push(2)
const item1 = queue.shift()
const item2 = queue.shift()
4.2 什么场景需要用队列?
需要用到先进先出的场景都会用到队列
常见的场景:
- 食堂排队打饭
- js异部中任务队列
- 计算最近请求次数
四、数据结构之链表
4.1 链表简介
什么是链表?
- 多个元素组成的列表
- 元素之间不连续,用next指针相连。
链表 vs 数组
- 数组增删非首尾元素. 会移动元素的位置。
- 链表增删非首尾元素, 不会移动元素的位置, 只需更改next指针就行.
声明链表,对链表进行增删改查
// 创建链表
const a = { val: 'a' }
const b = { val: 'b' }
const c = { val: 'c' }
const d = { val: 'd' }
a.next = b
b.next = c
c.next = d
//遍历链表
let p = a
while (p) {
console.log(p.val)
p = p.next
}
//插入链表
let e = {
val: 'e',
}
c.next = e
e.next = d
//删除链表
c.next = d
4.2 leetcode题目练习
237. 删除链表中的节点
var deleteNode = function(node) {
node.val = node.next.val
node.next = node.next.next
};
206. 反转链表
//解题思路
//声明一个变量 遍历链表 进行赋值操作
var reverseList = function(head) {
let p1 = head
let p2 = null
while(p1) {
const temp = p1.next
p1.next = p2
p2 = p1
p1 = temp
}
return p2
};
83. 删除排序链表中的重复元素
//解题思路
//遍历链表,比较相邻元素的值
var deleteDuplicates = function(head) {
let p1 = head
while(p1 && p1.next) {
const temp = p1.next
if(p1.val == p1.next.val) {
p1.next = p1.next.next
} else {
p1 = temp
}
}
return head
};
141. 环形链表
//解题思路
//用一快一慢指针进行遍历链表, 如果指针能够相逢, 证明指针有环
//就和 在操场跑步一样, 跑步快的人 跑步慢的人 如果一直跑下去 他们总会在一个地方重逢
var hasCycle = function(head) {
let p1 = head
let p2 = head
while(p1 && p2 && p2.next) {
p1 = p1.next
p2 = p2.next.next
if(p1 === p2) {
return true
}
}
return false
};
4.3 前端与链表:JS 中的原型链
# 前端与链表:js中的原型链
- 原型链的本质是链表
- 原型上的节点是各种原型对象,eg:Function.prototype, Object.prototype ...
- 原型链通过 __proto__ 属性连接各种原型对象
obj -> Object.prototype -> null
func -> Function.prototype -> Object.prototype -> null
arr -> Array.prototype -> Object.prototype -> null
const obj = {}
const func = () => {}
const arr = []
obj.__proto__ === Object.prototype
## 原型链的知识点
- 如果 A 沿着原型链能找到 B.prototype,那么 A instanceof B 为 true
- 如果在 A 对象上没有找到 x 属性,那么会沿着原型链找 x 属性
## 面试题
instanceof 的原理,并用代码实现
const instanceof = (A, B) => {
let p = A;
while(p) {
if (p === B.prototype) return true
p = p.__proto__
}
return false
}
instanceof([], Array)
instanceof(1, Number)
2. 题2
var foo = {}
var F = function(){}
Object.prototype.a = 'value a'
Function.prototype.b = 'value b'
console.log(foo.a) // 'value a'
console.log(foo.b) // undefined
console.log(F.a) // 'value a'
console.log(F.b) // 'value b'
知识点:如果在 A 对象上没有找到 x 属性,那么会沿着原型链找 x 属性
解法:明确 foo 和 F 变量的原型链,沿着原型链找 a 属性和 b 属性
4.4 前端与链表:使用链表指针获取 JSON 的节点值
const json = {
a: {b: {c: 1} },
d: { e: 2 },
}
const path = ['a', 'b', 'c']
// 获取路径 path 对应的 json 中的值
let p = json // 先定义一个指针
path.forEach(k => {
p = p[k]
})
// 这是链表的遍历在前端日常工作中的应用示例
4.5 链表 - 技术要点
- 链表里的元素存储不是连续的,节点之间通过next 连接
- JavaScript 中没有链表这种数据结构,但可以用 Object 模拟链表
- 链表常用操作:修改next(增删节点),遍历链表(修改指针,让指针沿着链表走)
- js 中的原型链也是一个链表(沿着 __proto__ 属性走)
- 使用链表指针可以获取 JSON 的节点值 -- 使用链表指针,模拟遍历链表的算法,获取某个json节点的值
ps: 工作日每天更新,不解释为撒工作日了,懂的都懂.就像在起点写小说一样,自high行为,争取早日完结.