前端如何学习数据结构和算法(日更)

1,291 阅读3分钟

一、数据结构和算法简介

数据结构是什么:   计算机中存储数据、组织数据的结构

算法是什么:   一系列解决问题的清晰指令

说人话就是, 数据是食材, 数据结构是锅碗瓢盆, 算法是食谱

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 队列简介

什么是队列?  队列是一种先进先出的数据结构.

image.png

生活中队列?  排队买票,排队吃饭等等.

image.png

javascript代码演示

const queue = []
queue.push(1)
queue.push(2)
const item1 = queue.shift()
const item2 = queue.shift()

4.2 什么场景需要用队列?

需要用到先进先出的场景都会用到队列

常见的场景:

  • 食堂排队打饭
  • js异部中任务队列
  • 计算最近请求次数

四、数据结构之链表

4.1 链表简介

什么是链表?  

  1. 多个元素组成的列表
  2. 元素之间不连续,用next指针相连。

链表 vs 数组

  1. 数组增删非首尾元素. 会移动元素的位置。
  2. 链表增删非首尾元素, 不会移动元素的位置, 只需更改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.prototypereturn true
    p = p.__proto__
  }
  return false
}

instanceof([], Array)
instanceof(1Number)

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行为,争取早日完结.