JavaScript 数据结构与算法(持续更新)

JavaScript 数据结构与算法

  • 掌握 JS 内置的数据结构及背后的工作原理
  • 依据内置数据结构自定义创建其他数据结构(链表、堆栈、队列、二叉搜索树、有限队列、堆、图形等)
  • 理解不同数据结构的存在意义及背后工作原理
  • 学会比较不同数据结构在进行操作的时间复杂度
  • 掌握分析数据结构,优化数据结构的思考方式及实现过程
  • 学会分析在开发环境中对数据结构的选择

基础知识

lesson-1: 什么是数据结构&为什么需要数据结构

数据结构是计算机存储、组织数据的方式,允许我们管理数据

不同的场景需要不同的数据结构

  • 有序列表数据允许重复——Array
  • 无序列表数据不允许重复——Set
  • 无序数据键值对形式——Object
  • 有序键值对可遍历数据——Map

lesson-2: 数组

  • 保留插入顺序
  • 通过索引访问元素
  • 可遍历(for 循环)
  • 大小(长度)动态调整
  • 允许重复
  • 删除和查找元素比较耗费性能
const names = ['Summer', 'Henry', 'Lucy', 'Summer']

// 索引值从0开始
console.log(names[1])
console.log(names.length)

// for循环遍历
for (const name of names) {
  console.log(name)
}

// 添加元素
names.push('Lucy')
console.log(names.length)

// 查询元素
const lucyIndex = names.findIndex((name) => name === 'Lucy')
console.log(lucyIndex)

// 删除元素
names.splice(2, 1)
console.log(names)

lesson-3: 集合

  • 无序(存储和读取顺序可能不一样)
  • 通过方法访问和获取元素
  • 可遍历(for 循坏)
  • 大小(长度)动态调整
  • 不允许重复(要求元素唯一)
  • 删除和查找元素简单快捷
const ids = new Set()

// 添加元素
ids.add('a')
ids.add(1)
ids.add('b')
ids.add(1)
// Set(3) {'a', 1, 'b'}

// 集合遍历元素
for (const id of ids) {
  console.log(id)
}

// 集合访问元素
console.log(ids.has('a')) // true

// 集合删除元素
ids.delete('b')
console.log(ids) // Set(2) {'a', 1}
console.log(ids[0]) // undefined

lesson-4: 数组 VS 集合

数组

  • 总是使用数组
  • 如果强调排序和元素重复,则必须使用

集合

  • 仅在顺序无关紧要且仅要求唯一值时可用
  • 与数组相比,可以简化数据访问(例如查找,删除)

lesson-5: 对象

  • 无序的键值对数据
  • 通过键(属性)访问元素
  • 不可遍历(除了 for-in 循环)
  • 键是唯一的,值不是
  • 键必须是字符串,数字或符号
  • 可以存储数据和 “函数”(方法)
const person = {
  name: 'John',
  age: 33,
  hobbies: ['Sports', 'Music'],
  greeting() {
    console.log('Hello, I am ' + this.name)
  },
}

console.log(person[0]) // undefined
console.log(person['name'])
console.log(person.name)

// 添加属性
person.sex = 'male'
// 删除属性
delete person.age
console.log(person)

// 有方法,添加功能
person.greeting()

lesson-6: 映射

  • 有序的键值对数据
  • 通过键访问元素
  • 可遍历(使用 for 循环)
  • 键是唯一的,值不是
  • 键可是各种类型的值(包括对象、数组)
  • 纯数据存储,针对数据访问进行了优化
const resultData = new Map()

// 添加键值对 set
resultData.set('average', 1.6)
resultData.set('lastResult', null)

const person = { name: 'John', age: 34 }

resultData.set(person, 1.24)

// for循环
for (const el of resultData) {
  console.log(el)
}

// key相同情况
resultData.set('average', 23)

// 获取或者访问值
console.log(resultData.get('lastResult'))
console.log(resultData.lastResult) //undefined

// 删除
console.log(resultData.delete('average'))

// 删除key为对象的时候
resultData.delete({ name: 'John', age: 34 })

console.log(resultData)

lesson-7: 对象&映射

对象

  • 非常通用的构造和数据存储结构
  • 如果添加额外的功能则必须使用

映射

  • 专注于数据存储
  • 与对象相比,可以简化数据访问

lesson-8: 弱映射&弱集合

集合和映射的变体 → 值和键仅 “弱引用” → 如果未在应用程序的其他任何地方使用,垃圾回收则可以删除值和键

lesson-9: 自定义数据结构-链表

每一个元素都知道下一个元素

可以有效地调整大小并在列表的开头和结尾插入

链表

lesson-10: 链表构造函数 & append 方法
class LinkedList {
  constructor() {
    this.head = null // 链表中第一个节点
    this.tail = null // 链表中最后一个节点
  }

  // append 追加节点(末尾添加)
  append(value) {
    const newNode = { value: value, next: null }
    if (this.tail) {
      this.tail.next = newNode
    }
    this.tail = newNode
    if (!this.head) {
      this.head = newNode
    }
  }
}

const linkedList1 = new LinkedList()
lesson-11: 创建输出链表的方法
// 以数组方式输出节点
toArray() {
  const elements = []
  let curNode = this.head
  while (curNode) {
    elements.push(curNode)
    curNode = curNode.next
  }
  return elements
}
lesson-12: prepend 方法
// prepend 前置节点(头部添加)
prepend(value) {
  const newNode = { value: value, next: this.head }
  this.head = newNode
  if (!this.tail) {
    this.tail = newNode
  }
}
lesson-13: delete 方法
// delete 删除节点
delete(value) {
  if (!this.head) {
    return
  }
  while (this.head && this.head.value === value) {
    this.head = this.head.next
  }
  let curNode = this.head
  while (curNode.next) {
    if (curNode.next.value === value) {
      curNode.next = curNode.next.next
    } else {
      curNode = curNode.next
    }
  }
  if (this.tail.value === value) {
    this.tail = curNode
  }
}
lesson-14: find & insertAfter 方法
// find 节点查询
find(value) {
  if (!this.head) {
    return null
  }

  let curNode = this.head
  while (curNode) {
    if (curNode.value === value) {
      return curNode
    }
    curNode = curNode.next
  }
  return null
}

// insertAfter 某个节点后面插入
insertAfter(value, afterValue) {
  const existingNode = this.find(afterValue)
  if (existingNode) {
    const newNode = { value: value, next: existingNode.next }
    existingNode.next = newNode
  }
}

总结

class LinkedList {
  constructor() {
    this.head = null // 链表中第一个节点
    this.tail = null // 链表中最后一个节点
  }

  // append 追加节点(末尾添加)
  append(value) {
    const newNode = { value: value, next: null }
    if (this.tail) {
      this.tail.next = newNode
    }
    this.tail = newNode
    if (!this.head) {
      this.head = newNode
    }
  }

  // prepend 前置节点(头部添加)
  prepend(value) {
    const newNode = { value: value, next: this.head }
    this.head = newNode
    if (!this.tail) {
      this.tail = newNode
    }
  }

  // find 节点查询
  find(value) {
    if (!this.head) {
      return null
    }

    let curNode = this.head
    while (curNode) {
      if (curNode.value === value) {
        return curNode
      }
      curNode = curNode.next
    }
    return null
  }

  // insertAfter 某个节点后面插入
  insertAfter(value, afterValue) {
    const existingNode = this.find(afterValue)
    if (existingNode) {
      const newNode = { value: value, next: existingNode.next }
      existingNode.next = newNode
    }
  }

  // delete 删除节点
  delete(value) {
    if (!this.head) {
      return
    }
    while (this.head && this.head.value === value) {
      this.head = this.head.next
    }
    let curNode = this.head
    while (curNode.next) {
      if (curNode.next.value === value) {
        curNode.next = curNode.next.next
      } else {
        curNode = curNode.next
      }
    }
    if (this.tail.value === value) {
      this.tail = curNode
    }
  }

  // 以数组方式输出节点
  toArray() {
    const elements = []
    let curNode = this.head
    while (curNode) {
      elements.push(curNode)
      curNode = curNode.next
    }
    return elements
  }
}

const linkedList1 = new LinkedList()

linkedList1.append(1)
linkedList1.append('Summer')
linkedList1.append('Summer')
linkedList1.append('Hello')
linkedList1.append(5)
linkedList1.append(true)
linkedList1.prepend('第一个元素')
linkedList1.prepend('第一个元素')

console.log(linkedList1.toArray())

linkedList1.delete('Summer')
linkedList1.delete('第一个元素')
linkedList1.delete(5)

console.log(linkedList1.toArray())
console.log(linkedList1.find('Summer'))
console.log(linkedList1.find(true))
lesson-15: 为什么需要链表

历史上(对于其他编程语言),主要原因是内存管理:不必事先指定(占用)的大小

如今,JavaScript 具有动态数组(内置动态调整大小),而内存并不是 JavaScript 应用程序中的主要问题

如果在列表的开头进行高频插入操作,那链表会很有用——链表比数组操作更快

leeson-16: 衡量性能(时间复杂度——大 O 符号)

表示代码执行时间的增长变化趋势

lesson-17: 链表时间复杂度 & 数组
链表(Linked List) 数组(Arrays)
元素访问 O(n) O(1)
末尾插入 尾部:O(1),非尾部:O(n) O(1)
头部插入 O(1) O(n)
中间插入 搜索时间 + O(1) O(n)
元素搜索 O(n) O(n)

列表和表格

什么是列表和表格数据结构

栈 & 队列

哈希表

lesson-18: 什么是“列表和表格结构”

列表(Lists)——值的集合,例如:数组,集合,链表;适合存储通过位置(通过索引或搜索)检索的值,也适合循环

表格(Tables)——键值对的集合,例如:对象,映射;适合存储通过键检索的值,不关注循环

lesson-19: 内置表格和列表

// 列表结构——数组
const shoppingList = ['Apple', 'Banana', 'Orange']

const thirdItem = shoppingList[2]

for (const item of shoppingList) {
  // 得到每一项采购的水果名称
}

// 表格结构——对象
const citizens = {
  123: {
    name: 'summer',
    age: 26,
    sex: 'female',
    address: 'xxxx',
    birthdate: 'xxx',
  },
  456: {},
}

const summerData = citizens['123'] // ==> summer相关的信息

列表

堆栈
lesson-20: 列表——堆栈(Stack)

简化的数组,后进先出(Last In First Out,LIFO)

lesson-21: 自定义堆栈结构(数组)
class Stack {
  constructor() {
    this.items = []
  }

  push(value) {
    this.items.push(value)
  }

  pop() {
    return this.items.pop()
  }

  isEmpty() {
    return this.items.length === 0
  }

  toArray() {
    return this.items.slice()
  }
}

const stack = new Stack()

stack.push('关冰箱门!')
stack.push('推入大象')
stack.push('开冰箱门!')

console.log(stack.toArray())

console.log(stack.pop())
console.log(stack.toArray())
console.log(stack.pop())
console.log(stack.toArray())
console.log(stack.pop())
console.log(stack.toArray())
lesson-22: 自定义堆栈结构(链表)
import { LinkedList } from './linked-list.js'

class Stack {
  constructor() {
    this.list = new LinkedList()
  }

  push(value) {
    this.list.prepend(value)
  }

  pop() {
    return this.list.deleteHead()
  }

  isEmpty() {
    return !this.list.head
  }

  toArray() {
    return this.list.toArray()
  }
}
// linked-list.js
export class LinkedList {
  constructor() {
    this.head = null // 链表中第一个节点
    this.tail = null // 链表中最后一个节点
  }

  // append 追加节点(末尾添加)
  append(value) {
    const newNode = { value: value, next: null }
    if (this.tail) {
      this.tail.next = newNode
    }
    this.tail = newNode
    if (!this.head) {
      this.head = newNode
    }
  }

  // prepend 前置节点(头部添加)
  prepend(value) {
    const newNode = { value: value, next: this.head }
    this.head = newNode
    if (!this.tail) {
      this.tail = newNode
    }
  }

  // find 节点查询
  find(value) {
    if (!this.head) {
      return null
    }

    let curNode = this.head
    while (curNode) {
      if (curNode.value === value) {
        return curNode
      }
      curNode = curNode.next
    }
    return null
  }

  // insertAfter 某个节点后面插入
  insertAfter(value, afterValue) {
    const existingNode = this.find(afterValue)
    if (existingNode) {
      const newNode = { value: value, next: existingNode.next }
      existingNode.next = newNode
    }
  }

  // delete 删除节点
  delete(value) {
    if (!this.head) {
      return
    }
    while (this.head && this.head.value === value) {
      this.head = this.head.next
    }
    let curNode = this.head
    while (curNode.next) {
      if (curNode.next.value === value) {
        curNode.next = curNode.next.next
      } else {
        curNode = curNode.next
      }
    }
    if (this.tail.value === value) {
      this.tail = curNode
    }
  }

  // 删除头部节点
  deleteHead() {
    if (!this.head) {
      return null
    }
    const deleteHead = this.head
    if (this.head.next) {
      this.head = this.head.next
    } else {
      this.head = null
      this.tail = null
    }

    return deleteHead
  }

  // 以数组方式输出节点
  toArray() {
    const elements = []
    let curNode = this.head
    while (curNode) {
      elements.push(curNode)
      curNode = curNode.next
    }
    return elements
  }
}
lessson-23: 堆栈时间复杂度 & 数组
堆栈(Stacks) 数组(Arrays)
元素访问 O(1)仅限于 “栈顶元素” O(1)
末尾插入 O(1) O(1)
头部插入 O(n) 会导致 “数据丢失” O(n)
中间插入 O(n) 会导致 “数据丢失” O(n)
元素搜索 O(n) 会导致 “数据丢失” O(n)
队列

简化的数组,先进先出(First In First Out,FIFO)

lesson-24: 自定义队列结构(数组实现)
class Queue {
  constructor() {
    this.items = []
  }

  enqueue(value) {
    this.items.unshift(value)
  }

  dequeue() {
    return this.items.pop()
  }

  isEmpty() {
    return this.items.length === 0
  }

  toArray() {
    return this.items.slice()
  }
}

const queue = new Queue()

queue.enqueue('第1号')
queue.enqueue('第2号')
queue.enqueue('第3号')

console.log(queue.toArray())

console.log(queue.dequeue())
console.log(queue.toArray())
console.log(queue.dequeue())
console.log(queue.toArray())
console.log(queue.dequeue())
console.log(queue.toArray())
lesson-25: 自定义队列结构(链表实现)
import { LinkedList } from './linked-list.js'

class Queue {
  constructor() {
    this.list = new LinkedList()
  }

  enqueue(value) {
    this.list.append(value)
  }

  dequeue() {
    return this.list.deleteHead()
  }

  isEmpty() {
    return !this.list.head
  }

  toArray() {
    return this.list.toArray()
  }
}
// linked - list.js
export class LinkedList {
  constructor() {
    this.head = null // 链表中第一个节点
    this.tail = null // 链表中最后一个节点
  }

  // append 追加节点(末尾添加)
  append(value) {
    const newNode = { value: value, next: null }
    if (this.tail) {
      this.tail.next = newNode
    }
    this.tail = newNode
    if (!this.head) {
      this.head = newNode
    }
  }

  // prepend 前置节点(头部添加)
  prepend(value) {
    const newNode = { value: value, next: this.head }
    this.head = newNode
    if (!this.tail) {
      this.tail = newNode
    }
  }

  // find 节点查询
  find(value) {
    if (!this.head) {
      return null
    }

    let curNode = this.head
    while (curNode) {
      if (curNode.value === value) {
        return curNode
      }
      curNode = curNode.next
    }
    return null
  }

  // insertAfter 某个节点后面插入
  insertAfter(value, afterValue) {
    const existingNode = this.find(afterValue)
    if (existingNode) {
      const newNode = { value: value, next: existingNode.next }
      existingNode.next = newNode
    }
  }

  // delete 删除节点
  delete(value) {
    if (!this.head) {
      return
    }
    while (this.head && this.head.value === value) {
      this.head = this.head.next
    }
    let curNode = this.head
    while (curNode.next) {
      if (curNode.next.value === value) {
        curNode.next = curNode.next.next
      } else {
        curNode = curNode.next
      }
    }
    if (this.tail.value === value) {
      this.tail = curNode
    }
  }

  // 删除头部节点
  deleteHead() {
    if (!this.head) {
      return null
    }
    const deleteHead = this.head
    if (this.head.next) {
      this.head = this.head.next
    } else {
      this.head = null
      this.tail = null
    }

    return deleteHead
  }

  // 以数组方式输出节点
  toArray() {
    const elements = []
    let curNode = this.head
    while (curNode) {
      elements.push(curNode)
      curNode = curNode.next
    }
    return elements
  }
}
lesson-26: 队列时间复杂度 & 数组
队列(Queues) 数组(Arrays)
元素访问 O(1) 仅限 “第一个元素 O(1)
末尾插入 O(n) 会导致 “数据丢失” O(1)
头部插入 O(1) O(n)
中间插入 O(n) 会导致 “数据丢失” O(n)
元素搜索 O(n) 会导致 “数据丢失” O(n)

表格——哈希表

lesson-27: 为什么需要表格

现有的 JavaScript “对象” 都是基于哈希表实现的

JS 中不需要自己创建哈希表

const words = 'ehehlloworld'

// 双重for循环
// function findFirst(str) {
//   for (let i = 0; i < str.length; i++) {
//     for (let j = i + 1; j < str.length; j++) {
//       if (str[i] === str[j]) {
//         return str[i]
//       }
//     }
//   }
// }
// 大O符号:O(n^2)
// console.log(findFirst(words))

// 通过对象(哈希表)
function findFirstRep(str) {
  const table = {}
  for (const word of str) {
    if (table[word]) {
      return word
    }
    table[word] = 1
  }
}

// 大O符号:O(n)
console.log(findFirstRep(words))
lesson-28: 自定义简单的哈希表
class HashTable {
  constructor() {
    this.size = 1000
    this.buckets = Array(1000).fill(null)
  }

  hash(key) {
    let hash = 0
    for (const char of key) {
      hash += char.charCodeAt(0)
    }
    return hash % this.size
  }

  set(key, value) {
    const keyHash = this.hash(key)
    this.buckets[keyHash] = value
  }

  get(key) {
    const keyHash = this.hash(key)
    return this.buckets[keyHash]
  }
}

const words = 'helloworld'
function findFirstRep(str) {
  const table = new HashTable()
  for (const word of str) {
    if (table.get(word)) {
      return word
    }
    table.set(word, 1)
  }
}

console.log(findFirstRep(words))
lesson-29: 哈希碰撞
class HashTable {
  constructor() {
    this.size = 16
    this.buckets = Array(16).fill(null)
  }

  hash(key) {
    let hash = 0
    for (const char of key) {
      hash += char.charCodeAt(0)
    }
    return hash % this.size
  }

  set(key, value) {
    const keyHash = this.hash(key)
    this.buckets[keyHash] = value
  }

  get(key) {
    const keyHash = this.hash(key)
    return this.buckets[keyHash]
  }

  showInfo() {
    for (const key in this.buckets) {
      if (this.buckets[key] !== null) {
        console.log(key, this.buckets[key])
      }
    }
  }
}

const table = new HashTable()

for (const char of 'abcde') {
  table.set(char, char)
}

for (const char of 'fghijk') {
  table.set(char, char)
}

for (const char of 'lmnopq') {
  table.set(char, char)
}

console.log(table.showInfo())
lesson-30: 哈希碰撞——链地址法
class HashTable {
  constructor() {
    this.size = 16
    this.buckets = Array(16)
      .fill(null)
      .map(() => [])
  }

  hash(key) {
    let hash = 0
    for (const char of key) {
      hash += char.charCodeAt(0)
    }
    return hash % this.size
  }

  set(key, value) {
    const keyHash = this.hash(key)
    const bucketArray = this.buckets[keyHash]
    const storedElment = bucketArray.find((element) => {
      return element.key === key
    })
    if (storedElment) {
      storedElment.val = value
    } else {
      bucketArray.push({ key: key, val: value })
    }
  }

  get(key) {
    const keyHash = this.hash(key)
    const bucketArray = this.buckets[keyHash]
    const storedElment = bucketArray.find((element) => {
      return element.key === key
    })
    return storedElment
  }

  showInfo() {
    for (const key in this.buckets) {
      if (this.buckets[key] !== null) {
        console.log(key, this.buckets[key])
      }
    }
  }
}

lesson-31: 哈希碰撞——开放地址法

class HashTable {
  constructor() {
    this.size = 100
    this.buckets = Array(100).fill(null)
  }

  hash(key) {
    let hash = 0
    for (const char of key) {
      hash += char.charCodeAt(0)
    }
    return hash % this.size
  }

  set(key, value) {
    let keyHash = this.hash(key)
    if (this.buckets[keyHash] === null || this.buckets[keyHash].key === key) {
      this.buckets[keyHash] = { key: key, val: value }
    } else {
      while (this.buckets[keyHash] !== null) {
        keyHash++
      }
      this.buckets[keyHash] = { key: key, val: value }
    }
  }

  get(key) {
    const keyHash = this.hash(key)
    for (let i = keyHash; i < this.buckets.length; i++) {
      if (!this.buckets[i]) {
        continue
      }
      if (this.buckets[i].key === key) {
        return this.buckets[i].value
      }
    }
    return undefined
  }

  showInfo() {
    for (const key in this.buckets) {
      if (this.buckets[key] !== null) {
        console.log(key, this.buckets[key])
      }
    }
  }
}
lesson-31: 哈希表时间复杂度 & 数组 & 对象
哈希表(Hash Table) 数组(Arrays) 对象(Objects)
元素访问 理论上 O(1) / 哈希碰撞 O(n) O(1) O(1)
末尾插入 理论上 O(1) / 哈希碰撞 O(n) O(1) O(1)
头部插入 理论上 O(1) / 哈希碰撞 O(n) O(n) O(1)
中间插入 理论上 O(1) / 哈希碰撞 O(n) O(n) O(1)
元素搜索 理论上 O(1) / 哈希碰撞 O(n) O(n) O(1)

  • 什么是树形结构
  • 二叉搜索树 & AVL 树
  • 字典树

lesson-32: 什么是树形结构

树形结构指的是数据元素之间存在着 “一对多” 的树形关系的数据结构,是一类重要的非线性数据结构。(没有周期,没有循环)

  • 结点/顶点:包含值的结构(树中的数据元素)
  • 边:连接两个结点
  • 根结点:树中最上面的结点
  • 子树:嵌套树/子树的根结点不是主树的根结点,子树就是树的其中一个节点以及其下面的所有的结点所构成的树
  • 树叶:没有任何子结点的结点(即没有子树,树的尽头)
  • 路径:连接两个结点的一系列结点和边
  • 距离:两个结点之间的边数
  • 父结点/子结点:两个直接连接的结点,父结点在子结点之上
  • 祖先/后裔:通过多个父子路径连接的两个结点
  • 兄弟:具有相同父亲的结点称为兄弟
  • 结点的度:结点所拥有的子树的个数称之为结点的度
  • 结点的层次:从根结点到树中某结点所经路径上的分支树称为该结点的层次(根结点层次为 0)
  • 树的深度:树中结点的最大层次数(垂直空间)
  • 树的广度:树的叶子数(水平空间)
  • 大小:树中的结点总数

lesson-33: 代码实现基础的树形结构

class Node {
  constructor(value, parentNode = null) {
    this.children = []
    this.value = value
    this.parent = parentNode
  }

  addNode(value) {
    const node = new Node(value, this)
    this.children.push(node)
    return { node: node, index: this.children.length - 1 }
  }

  removeNode(index) {
    this.children.splice(index, 1)
  }
}

class Tree {
  constructor(rootValue) {
    this.root = new Node(rootValue)
  }
}

const filesystem = new Tree('/')
const pagesDocumentNode = filesystem.root.addNode('/pages文稿')
const deskTopNode = filesystem.root.addNode('/桌面')

pagesDocumentNode.node.addNode('/学习')
pagesDocumentNode.node.addNode('/工作')
deskTopNode.node.addNode('数据结构.key')

console.log(filesystem)

lesson-34: 递归实现文件树

class Node {
  constructor(value, parentNode = null) {
    this.children = []
    this.value = value
    this.parent = parentNode
  }

  addNode(value) {
    const segments = value.split('/')
    if (segments.length === 0) {
      return
    }
    if (segments.length === 1) {
      const node = new Node(value, this)
      this.children.push(node)
      return { node: node, index: this.children.length - 1 }
    }

    const existingChildNode = this.children.find(
      (child) => child.value === segments[0]
    )

    if (existingChildNode) {
      existingChildNode.addNode(segments.slice(1).join('/'))
    } else {
      const node = new Node(segments[0], this)
      node.addNode(segments.slice(1).join('/'))
      this.children.push(node)
      return { node: node, index: this.children.length - 1 }
    }
  }

  removeNode(value) {
    const segments = value.split('/')
    if (segments.length === 0) {
      return
    }
    if (segments.length === 1) {
      const existingChildNode = this.children.findIndex(
        (child) => child.value === segments[0]
      )
      if (existingChildNode < 0) {
        throw new Error('无法找到匹配的值!')
      }
      this.children.splice(existingChildNode, 1)
    }
    if (segments.length > 1) {
      const existingChildNode = this.children.find(
        (child) => child.value === segments[0]
      )
      if (!existingChildNode) {
        throw new Error('无法找到匹配的路径!路径为:' + segments[0])
      }
      existingChildNode.removeNode(segments.slice(1).join('/'))
    }
  }
}

class Tree {
  constructor(rootValue) {
    this.root = new Node(rootValue)
  }
  add(path) {
    this.root.addNode(path)
  }
  remove(path) {
    this.root.removeNode(path)
  }
}

const filesystem = new Tree('/')
filesystem.add('pages文稿/学习/前端学习路线.pdf')
filesystem.add('pages文稿/工作/code.js')
filesystem.add('下载/ps.dmg/ps.exe')
filesystem.add('下载/ps.dmg/ps.txt')
filesystem.remove('pages文稿/工作/code.js')
filesystem.remove('下载/ps.dmg/ps.txt')

console.log(filesystem)

lesson-35: 树形结构时间复杂度 & 数组

树(Tree) 数组(Arrays)
访问/搜索 最差情况 O(n) 有索引:O(1)/搜索 O(n)
插入 最差情况 O(n) 末尾 O(1)/头部 O(n)
清除 最差情况 O(n) 末尾 O(1)/头部 O(n)

lesson-36: 遍历树形结构

深度优先(DFS):从根结点开始挖掘树并逐步探索同级树

广度优先(BFS):在深入挖掘树之前,先查找所有同级值

class Node {
  constructor(value, parentNode = null) {
    this.children = []
    this.value = value
    this.parent = parentNode
  }

  //深度优先搜索
  findDFS(value) {
    console.log(this)
    for (const child of this.children) {
      if (child.value === value) {
        return child
      }
      const nestedChildNode = child.find(value)
      if (nestedChildNode) {
        return nestedChildNode
      }
    }
  }
  //广度优先搜索
  findBFS(value) {
    console.log(this)
    for (const child of this.children) {
      if (child.value === value) {
        return child
      }
    }
    for (const child of this.children) {
      const nestedChildNode = child.find(value)
      if (nestedChildNode) {
        return nestedChildNode
      }
    }
  }
}

class Tree {
  constructor(rootValue) {
    this.root = new Node(rootValue)
  }
  findDFS(value) {
    if (this.root.value === value) {
      return this.root
    }
    return this.root.findDFS(value)
  }
  findBFS(value) {
    if (this.root.value === value) {
      return this.root
    }
    return this.root.findBFS(value)
  }
}

lesson-37: 二叉搜索树(Binary Search Tree)

class Node {
  constructor(value) {
    this.value = value
    this.right = null
    this.left = null
    this.parent = null
  }

  add(value) {
    if (this.value === null) {
      this.value = value
      return
    }
    if (this.value < value) {
      if (this.right) {
        this.right.add(value)
        return
      }
      const newNode = new Node(value)
      newNode.parent = this
      this.right = newNode
      return
    }
    if (this.value > value) {
      if (this.left) {
        this.left.add(value)
        return
      }
      const newNode = new Node(value)
      newNode.parent = this
      this.left = newNode
      return
    }
  }

  remove(value) {
    const identifiedNode = this.find(value)
    if (!identifiedNode) {
      throw new Error('无法找到匹配的结点值')
    }
    //删除的结点是树叶的情况
    if (!identifiedNode.left && !identifiedNode.right) {
      const identifiedParent = identifiedNode.parent
      identifiedParent.removeChild(identifiedNode)
      return
    }
    //删除的结点有子结点
    if (identifiedNode.left && identifiedNode.right) {
      const nextBiggerNode = identifiedNode.right.findNext()
      if (nextBiggerNode.value !== identifiedNode.right.value) {
        this.remove(nextBiggerNode.value)
        identifiedNode.value = nextBiggerNode.value
      } else {
        identifiedNode.value = identifiedNode.right.value
        identifiedNode.right = identifiedNode.right.right
      }
    } else {
      const childNode = identifiedNode.left || identifiedNode.right
      identifiedNode.left = childNode.left
      identifiedNode.right = childNode.right
      identifiedNode.value = childNode.value
    }
    if (identifiedNode.left) {
      identifiedNode.left.parent = identifiedNode
    }
    if (identifiedNode.right) {
      identifiedNode.right.parent = identifiedNode
    }
  }

  removeChild(node) {
    if (this.left && this.left === node) {
      this.left = null
      return
    }
    if (this.right && this.right === node) {
      this.right = null
      return
    }
  }

  findNext() {
    if (!this.left) {
      return this
    }
    return this.left.findNext()
  }

  find(value) {
    if (this.value === value) {
      return this
    }
    if (this.value < value && this.right) {
      return this.right.find(value)
    }
    if (this.value > value && this.left) {
      return this.left.find(value)
    }
  }
}

class Tree {
  constructor() {
    this.root = new Node(null)
  }

  add(value) {
    this.root.add(value)
  }

  remove(value) {
    this.root.remove(value)
  }

  find(value) {
    return this.root.find(value)
  }
}

你可能感兴趣的:(JavaScript,数据结构与算法,javascript,前端,数据结构)