当前位置:   article > 正文

数据结构在JavaScript中的体现

数据结构在JavaScript中的体现

一.概述


        数据结构是计算机中存储、组织数据的方式。通常情况下,精心选择的数据结构可以带来最优效率的算法,其实算法并不是一个很高级的东西,它充斥在每一种代码组织方式中;而且各种语言关于数据结构方面的内容都是大同小异的,会了一种语言的数据结构方面的内容就可以快速掌握其它语言的的数据结构;有一部分人对JavaScript有偏见,觉得这种语言没有档次,但又有多少人在学完JavaScript敢说精通它。

二.见识数据结构


(1)>>数组


        你没有看错,常见的数组就是一种数据结构,对于数组就是掌握一下它的各种方法,如push(),pop(),shift(),unshift(),indexOf(),sort(),slice(),splice()等,在这里就不赘述了。

(2)>>栈


 A.图解

               遵循的是后进先出

  B.封装代码 

  1. class Stack {
  2. //加#是为了保证其是私有属性,不让外界随意调用
  3. #items = [];
  4. pop() {
  5. return this.#items.pop()
  6. }
  7. push(data) {
  8. return this.#items.push(data)
  9. }
  10. //返回栈顶
  11. peek() {
  12. return this.#items.at(-1)
  13. }
  14. isEmpty() {
  15. return this.#items.length > 0 ? false : true
  16. }
  17. size() {
  18. return this.#items.length
  19. }
  20. clear() {
  21. this.#items = []
  22. }
  23. toString(){
  24. return this.#items.join(' ')
  25. }
  26. }

C.应用场景

        将十进制数转换为其它进制的数时用到了辗转相除法,刚好是把得到的余数从后到前输出得到对应进制下的数,如果把这些数从前到后推到栈里,再取出来岂不妙哉。

  1. function convert(decNumber, binaryNumber) {
  2. let number = decNumber
  3. let stack = new Stack()
  4. let string = ''
  5. //防止转换成十六进制的时候乱码
  6. let baseString = '0123456789ABCDEF'
  7. while (number > 0) {
  8. stack.push(number % binaryNumber)
  9. number = Math.floor(number / binaryNumber)
  10. }
  11. while (!(stack.isEmpty())) {
  12. string += baseString[stack.pop()]
  13. }
  14. return string
  15. }
  16. console.log(convert(50, 2));

(3)>>队列


 A.图解

        先进先出,后端进,前端出,类似于排队

 B.封装代码

             有缺点的封装方式
  1. class Queue {
  2. #items = [];
  3. //一旦元素变多,还用shift会造成运行效率低
  4. deleteQueue() {
  5. return this.#items.shift()
  6. }
  7. enterQueue(data) {
  8. return this.#items.push(data)
  9. }
  10. //返回队头
  11. frontQueue() {
  12. return this.#items.at(0)
  13. }
  14. isEmpty() {
  15. return this.#items.length > 0 ? false : true
  16. }
  17. size() {
  18. return this.#items.length
  19. }
  20. clear() {
  21. this.#items = []
  22. }
  23. toString() {
  24. return this.#items.join(' ')
  25. }
  26. }
        修正之后的封装方式 
  1. class Queue {
  2. #items = {};
  3. #count = 0;
  4. #lowCount = 0;
  5. //出队
  6. deleteQueue() {
  7. if (this.isEmpty()) {
  8. return undefined
  9. }
  10. let res = this.#items[this.#lowCount]
  11. delete this.#items[this.#lowCount]
  12. this.#lowCount++
  13. return res
  14. }
  15. //进队
  16. enterQueue(data) {
  17. this.#items[this.#count] = data
  18. this.#count++
  19. }
  20. //返回队头
  21. frontQueue() {
  22. return this.#items[this.#lowCount]
  23. }
  24. isEmpty() {
  25. return this.size === 0
  26. }
  27. size() {
  28. return this.#count - this.#lowCount
  29. }
  30. clear() {
  31. this.#items = {}
  32. this.#count = 0
  33. this.#lowCount = 0
  34. }
  35. toString() {
  36. let str = ""
  37. for (let i = this.#lowCount; i < this.#count; i++) {
  38. str += `${this.#items[i]} `
  39. }
  40. return str.trim()
  41. }
  42. }

C.应用场景 

类似于击鼓传花,传指定次数,最终东西在谁手上谁淘汰,一直淘汰到队列里只剩一个人为最终的胜者

  1. function game(list, num) {
  2. let queue = new Queue()
  3. for (let i = 0; i < list.length; i++) {
  4. queue.enterQueue(list[i])
  5. }
  6. while (queue.size() > 1) {
  7. for (i = 0; i < num; i++) {
  8. queue.enterQueue(queue.deleteQueue())
  9. }
  10. console.log(queue.deleteQueue(), '淘汰');
  11. }
  12. console.log(queue.deleteQueue());
  13. //最终获胜者
  14. return queue.deleteQueue()
  15. }
  16. game(['aks', 'uej', 'jsm', 'duj', 'llq'], 7)

D.双端队列

与一般的队列有所不同的情况是,可以从队头进队,队尾可以出队,简而言之就是两端都能进出队伍。

  1. class DeQueue {
  2. #items = {};
  3. #count = 0;
  4. #lowCount = 0;
  5. removeFront() {
  6. if (this.isEmpty()) {
  7. return undefined
  8. }
  9. let res = this.#items[this.#lowCount]
  10. delete this.#items[this.#lowCount]
  11. this.#lowCount++
  12. return res
  13. }
  14. addBack(data) {
  15. this.#items[this.#count] = data
  16. this.#count++
  17. }
  18. //在队头添加
  19. addFront(data) {
  20. if (this.isEmpty()) {
  21. this.addBack()
  22. } else {
  23. if (this.#lowCount > 0) {
  24. this.#lowCount--
  25. this.#items[this.#lowCount] = data
  26. } else {
  27. for (let i = this.#count; i > 0; i--) {
  28. this.#items[i] = this.#items[i - 1]
  29. }
  30. this.#items[0] = data
  31. this.#count++
  32. }
  33. }
  34. }
  35. //在队尾删除
  36. removeBack() {
  37. if (this.isEmpty()) {
  38. return
  39. } else {
  40. this.#count--
  41. let res = this.#items[this.#count]
  42. delete this.#items[this.#count]
  43. return res
  44. }
  45. }
  46. //返回队头
  47. peekFront() {
  48. return this.#items[this.#lowCount]
  49. }
  50. //返回队尾
  51. peekBack() {
  52. return this.#items.at(-1)
  53. }
  54. isEmpty() {
  55. return this.size === 0
  56. }
  57. size() {
  58. return this.#count - this.#lowCount
  59. }
  60. clear() {
  61. this.#items = {}
  62. this.#count = 0
  63. this.#lowCount = 0
  64. }
  65. toString() {
  66. let str = ""
  67. for (let i = this.#lowCount; i < this.#count; i++) {
  68. str += `${this.#items[i]} `
  69. }
  70. return str.trim()
  71. }
  72. }
  73. //回文应用
  74. function test(str) {
  75. //将输入有空格的字符串转化为无空格的
  76. const lowStr = str.toLocaleLowerCase().split(' ').join('')
  77. let dequeue = new DeQueue()
  78. for (let i = 0; i < lowStr.length; i++) {
  79. dequeue.addBack(lowStr[i])
  80. }
  81. while (dequeue.size() > 1) {
  82. if (dequeue.removeFront() !== dequeue.removeBack()) {
  83. console.log('不是回文');
  84. break
  85. } else {
  86. console.log('是回文结构');
  87. }
  88. }
  89. }
  90. test('dadadb') //不是回文

(4)>>链表


1.单链表

A.图解

        可以把next看成一个指针,其能指向表中下一个存储的数据

B.封装代码
  1. //根据节点的需要创建相应的元素
  2. class Node {
  3. constructor(element) {
  4. this.element = element
  5. this.next = null
  6. }
  7. }
  8. //单链表
  9. class LinkList {
  10. constructor() {
  11. this.head = null
  12. this.count = 0
  13. }
  14. //从表尾放入新节点
  15. push(element) {
  16. const node = new Node(element)
  17. if (this.head === null) {
  18. this.head = node
  19. } else {
  20. let current = this.head
  21. while (current.next != null) {
  22. current = current.next
  23. }
  24. current.next = node
  25. }
  26. this.count++
  27. }
  28. size() {
  29. return this.count
  30. }
  31. isEmpty() {
  32. return this.size() === 0
  33. }
  34. //指定位置删除
  35. removeAt(index) {
  36. if (index >= 0 && index < this.count) {
  37. let current = this.head
  38. if (index === 0) {
  39. this.head = this.head.next
  40. } else {
  41. let previous
  42. for (let i = 0; i < index; i++) {
  43. previous = current
  44. current = current.next
  45. }
  46. previous.next = current.next
  47. }
  48. this.count--
  49. return current.element
  50. }
  51. return undefined
  52. }
  53. //同样也是指定位置删除,但用到了后面封装的getNodeAt方法
  54. removeAt2(index) {
  55. if (index >= 0 && index < this.count) {
  56. let current = this.head
  57. if (index === 0) {
  58. this.head = this.head.next
  59. } else {
  60. let previous = this.getNodeAt(index - 1)
  61. current = previous.next
  62. previous.next = current.next
  63. }
  64. this.count--
  65. return current.element
  66. }
  67. return undefined
  68. }
  69. //根据索引值找到相应的节点
  70. getNodeAt(index) {
  71. if (index >= 0 && index < this.count) {
  72. let node = this.head
  73. for (let i = 0; i < index; i++) {
  74. node = node.next
  75. }
  76. return node
  77. }
  78. return undefined
  79. }
  80. indexOf(element) {
  81. let current = this.head
  82. for (let i = 0; i < this.count; i++) {
  83. //防止其直接输入一个对象变量
  84. if (JSON.stringify(element) === JSON.stringify(current.element)) {
  85. return i
  86. }
  87. current = current.next
  88. }
  89. return -1
  90. }
  91. //指定元素删除
  92. remove(element) {
  93. let index = this.indexOf(element)
  94. let removeElement = this.removeAt(index)
  95. return removeElement
  96. }
  97. insert(element, index) {
  98. if (index >= 0 && index <= this.count) {
  99. const node = new Node(element)
  100. if (index === 0) {
  101. let current = this.head
  102. node.next = current
  103. this.head = node
  104. this.count++
  105. } else {
  106. let previous = this.getNodeAt(index - 1)
  107. let current = previous.next
  108. previous.next = node
  109. node.next = current
  110. this.count++
  111. }
  112. return true
  113. }
  114. return false
  115. }
  116. }

2.双向链表

A.图解        

节点除了存储数据以外,还有两个指针分别指向前一个节点地址(prev)和下一个节点地址(next) 

B.封装代码
  1. //前人栽树,后人乘凉,继承自之前的Node类
  2. class DoubleNode extends Node {
  3. constructor(element) {
  4. super(element)
  5. this.prev = null
  6. }
  7. }
  8. //继承自LinkList类加方法重写
  9. class DoubleLinkList extends LinkList {
  10. constructor() {
  11. super()
  12. this.tail = null
  13. }
  14. push(element) {
  15. const doubleNode = new DoubleNode(element)
  16. if (this.head === null) {
  17. this.head = doubleNode
  18. this.tail = doubleNode
  19. } else {
  20. this.tail.next = doubleNode
  21. doubleNode.prev = this.tail
  22. this.tail = doubleNode
  23. }
  24. this.count++
  25. }
  26. insert(element, index) {
  27. if (index >= 0 && index <= this.count) {
  28. const doubleNode = new DoubleNode(element)
  29. if (index === 0) {
  30. //分链表是否有元素
  31. if (this.head === null) {
  32. this.head = doubleNode
  33. this.tail = doubleNode
  34. } else {
  35. this.head.prev = doubleNode
  36. doubleNode.next = this.head
  37. this.head = doubleNode
  38. }
  39. } else if (index === this.count) {
  40. this.tail.next = doubleLinkList
  41. doubleLinkList.prev = this.tail
  42. this.tail = doubleLinkList
  43. } else {
  44. let previous
  45. let current = this.head
  46. for (let i = 0; i < index; i++) {
  47. current = current.next
  48. previous = current.prev
  49. }
  50. current.prev = doubleNode
  51. doubleNode.next = current
  52. doubleNode.prev = previous
  53. previous.next = doubleNode
  54. }
  55. this.count++
  56. return true
  57. }
  58. return false
  59. }
  60. removeAt(index) {
  61. if (index >= 0 && index < this.count) {
  62. let current = this.head
  63. if (index === 0) {
  64. this.head = current.next
  65. if (this.count === 1) {
  66. this.tail = null
  67. } else {
  68. this.head.prev = null
  69. }
  70. } else if (index === this.count - 1) {
  71. current = this.tail
  72. this.tail = current.prev
  73. this.tail.next = null
  74. } else {
  75. current = this.getNodeAt(index)
  76. let previous = current.prev
  77. let future = current.next
  78. previous.next = future
  79. future.prev = previous
  80. }
  81. this.count--
  82. return current.element
  83. }
  84. return undefined
  85. }
  86. }

3.循环链表 

A.图解

B.封装代码
  1. //循环链表类继承自单链表类
  2. class CirculateLinkList extends LinkList {
  3. constructor() {
  4. super()
  5. }
  6. push(element) {
  7. const node = new Node(element)
  8. let current
  9. if (this.head === null) {
  10. this.head = node
  11. } else {
  12. current = this.getNodeAt(this.size() - 1)
  13. current.next = node
  14. }
  15. node.next = this.head
  16. this.count++
  17. }
  18. insert(element, index) {
  19. if (index >= 0 && index <= this.count) {
  20. const node = new Node(element)
  21. let current = this.head
  22. if (index === 0) {
  23. if (this.head === null) {
  24. this.head = node
  25. node.next = this.head
  26. } else {
  27. node.next = this.head
  28. current = this.getNodeAt(this.size() - 1)
  29. this.head = node
  30. current.next = node
  31. }
  32. } else {
  33. if (index === this.count) {
  34. current = this.getNodeAt(index - 1)
  35. current.next = node
  36. node.next = this.head
  37. } else {
  38. let previous = this.getNodeAt(index - 1)
  39. previous.next = node
  40. node.next = previous.next
  41. }
  42. }
  43. this.count++
  44. return true
  45. }
  46. return false
  47. }
  48. removeAt(index) {
  49. if (index >= 0 && index < this.count) {
  50. let current = this.head
  51. if (index === 0) {
  52. if (this.count === 1) {
  53. this.head = null
  54. } else {
  55. let last = this.getNodeAt(this.size() - 1)
  56. this.head = current.next
  57. last.next = this.head
  58. }
  59. } else {
  60. let previous = this.getNodeAt(index - 1)
  61. current = previous.next
  62. previous.next = current.next
  63. }
  64. this.count--
  65. return current.element
  66. }
  67. return undefined
  68. }
  69. }

(5)>>集合


Set结构,其实已经是JavaScript里封装好的,它是以值:值进行存储,且存入的值不能够重复

A.封装代码
  1. class Set {
  2. items = {}
  3. add(element) {
  4. if (!this.has(element)) {
  5. this.items[element] = element
  6. return true
  7. }
  8. return false
  9. }
  10. delete(element) {
  11. if (this.has(element)) {
  12. delete this.items[element]
  13. return true
  14. }
  15. return false
  16. }
  17. has(element) {
  18. return element in this.items
  19. }
  20. clear() {
  21. this.items = {}
  22. }
  23. size() {
  24. return Object.keys(this.items).length
  25. }
  26. values() {
  27. return Object.values(this.items)
  28. }
  29. }
  30. const mySet = new Set()
  31. let arr = [2, 1, 3, 3, 4, 5, 2, 1]
  32. arr.forEach((item) => {
  33. mySet.add(item)
  34. })
  35. console.log(mySet.values());//[ 1, 2, 3, 4, 5 ]
 B.原装set使用

Set结构生成的是一个迭代器的结构,并且由于是集合,可以进行相关的并集交集差集等运算,可以进行相关的,详细的相关使用请查看下方代码

  1. let mySet = new Set()
  2. mySet.add(1)
  3. mySet.add(2)
  4. mySet.add(3)
  5. let item = mySet.values() //生成的是一个迭代器
  6. console.log(item); //[Set Iterator] { 1, 2, 3 }
  7. console.log(item.next()); //{ value: 1, done: false }
  8. console.log(item.next()); //{ value: 2, done: false }
  9. console.log(item.next()); //{ value: 3, done: false }
  10. // for (let i of item) {
  11. // console.log(i);
  12. // } //1 2 3
  13. // console.log([...item]); //[ 1, 2, 3 ]
  14. // const A = new Set([1, 2, 3, 4])
  15. // const B = new Set([2, 3, 4, 5])
  16. // //取并集
  17. // const C = new Set([...A, ...B])
  18. // console.log(C); //Set(5) { 1, 2, 3, 4, 5 }
  19. // //取交集
  20. // const D = new Set([...A].filter(item => B.has(item)))
  21. // console.log(D); //Set(3) { 2, 3, 4 }
  22. // //取差集
  23. // const E = new Set([...A].filter(item => !B.has(item)))
  24. // console.log(E); //Set(1) { 1 }

(6)>>字典

字典和集合很相似,集合以[值,值]的形式存储元素,字 典则是以[键,值]的形式来存储元素。字典也称作映射、符号表或关联数组。

A.字典的封装

  1. class Dictionary {
  2. table = {}
  3. //保证当存入的键是对象的时候,强制它转换为json字符串的形式
  4. toStrFn(item) {
  5. if (item === null) {
  6. return "null"
  7. } else if (item === undefined) {
  8. return "undefined"
  9. } else if (typeof item === 'string' || item instanceof String) {
  10. return item
  11. }
  12. return JSON.stringify(item)
  13. }
  14. hasKey(key) {
  15. return this.table[this.toStrFn(key)] != null
  16. }
  17. set(key, value) {
  18. if (key != null && value != null) {
  19. const tableKey = this.toStrFn(key)
  20. // this.table[tableKey] = value
  21. this.table[tableKey] = new ValuePair(key, value)
  22. return true
  23. }
  24. return false
  25. }
  26. get(key) {
  27. let result = this.table[this.toStrFn(key)]
  28. return result == null ? undefined : result.value
  29. }
  30. remove(key) {
  31. if (this.hasKey(key)) {
  32. delete this.table[this.toStrFn(key)]
  33. return true
  34. }
  35. return false
  36. }
  37. keys() {
  38. return this.keyValues().map(item => item.key)
  39. }
  40. values() {
  41. return this.keyValues().map(item => item.value)
  42. }
  43. keyValues() {
  44. return Object.values(this.table)
  45. }
  46. size() {
  47. return Object.keys(this.table).length
  48. }
  49. isEmpty() {
  50. return this.size() === 0
  51. }
  52. clear() {
  53. this.table = {}
  54. }
  55. forEach(ch) {
  56. const valuePair = this.keyValues()
  57. for (let i = 0; i < valuePair; i++) {
  58. ch(valuePair[i].key, valuePair[i].value)
  59. }
  60. }
  61. }
  62. class ValuePair {
  63. constructor(key, value) {
  64. this.key = key
  65. this.value = value
  66. }
  67. }

B.散列表

HashMap 类,它是 Dictionary 类的一种散列表 实现方式。.散列算法的作用是尽可能快地在数据结构中找到一个值。这样在面对海量数据的时候可以加快查询的速度

  1. class HashTable {
  2. table = {}
  3. toStrFn(item) {
  4. if (item === null) {
  5. return "null"
  6. } else if (item === undefined) {
  7. return "undefined"
  8. } else if (typeof item === 'string' || item instanceof String) {
  9. return item
  10. }
  11. return JSON.stringify(item)
  12. }
  13. // hashCode(key) {
  14. // if (typeof key === 'number') {
  15. // return key
  16. // } else {
  17. // const tableKey = this.toStrFn(key)
  18. // let hash = 0
  19. // for (let i = 0; i < tableKey.length; i++) {
  20. // hash += tableKey.charCodeAt(i)
  21. // }
  22. // return hash % 35
  23. // }
  24. // }
  25. //将对应字符串或对象转成的字符串的ASCII值进行转换生成相应的数值
  26. hashCode(key) {
  27. const tableKey = this.toStrFn(key)
  28. let hash = 5381
  29. for (let i = 0; i < tableKey.length; i++) {
  30. hash = (hash * 33) + tableKey.charCodeAt(i)
  31. }
  32. return hash % 1013
  33. }
  34. set(key, value) {
  35. if (key != null && value != null) {
  36. let position = this.hashCode(key)
  37. this.table[position] = new ValuePair(key, value)
  38. }
  39. }
  40. get(key) {
  41. let result = this.table[this.hashCode(key)]
  42. return result == null ? undefined : result.value
  43. }
  44. remove(key) {
  45. if (this.table[this.hashCode(key)] != null) {
  46. delete this.table[this.hashCode(key)]
  47. return true
  48. }
  49. return false
  50. }
  51. }
  52. class ValuePair {
  53. constructor(key, value) {
  54. this.key = key
  55. this.value = value
  56. }
  57. }

3.ES6中的Map

  1. var mymap = new Map()
  2. mymap.set("name","kerwin")
  3. mymap.set({a:1},"aaaaaa")
  4. mymap.get("name")
  5. mymap.delete("name")

 (7)>>树


树是一种分层数据的抽象模型。

 1.二叉树

         二叉树中的节点最多只能有两个子节点:一个是左侧子节点,另一个是右侧子节点。

2.二叉搜索树

        二叉搜索树(BST)是二叉树的一种,但是只允许你在左侧节点存储(比父节点)小的值, 在右侧节点存储(比父节点)大的值。

  >>关于遍历

  • 中序遍历是一种以上行顺序访问 BST 所有节点的遍历方式,也就是以从最小到最大的顺序 访问所有节点。 中序遍历的一种应用就是对树进行排序操作。

  • 先序遍历是以优先于后代节点的顺序访问每个节点的。先序遍历的一种应用是打印一个结构 化的文档。(访问根节点,遍历左子树,遍历右子树)

  • 后序遍历则是先访问节点的后代节点,再访问节点本身。后序遍历的一种应用是计算一个目 录及其子目录中所有文件所占空间的大小。(简而言之就是在树中从最末尾的那一代开始从左到右打印,一直到整个树都被遍历完成)

>>关于移除要考虑的情况

A.封装代码
  1. class Node {
  2. constructor(key) {
  3. this.key = key
  4. this.left = null
  5. this.right = null
  6. }
  7. }
  8. class BST {
  9. constructor() {
  10. this.root = null
  11. }
  12. insert(key) {
  13. if (this.root == null) {
  14. this.root = new Node(key)
  15. } else {
  16. this.insertNode(this.root, key)
  17. }
  18. }
  19. insertNode(node, key) {
  20. if (this.compareFn(key, node.key) === -1) {
  21. if (node.left == null) {
  22. node.left = new Node(key)
  23. } else {
  24. this.insertNode(node.left, key)
  25. }
  26. } else {
  27. if (node.right == null) {
  28. node.right = new Node(key)
  29. } else {
  30. this.insertNode(node.right, key)
  31. }
  32. }
  33. }
  34. compareFn(a, b) {
  35. if (a === b) {
  36. return 0
  37. }
  38. return a < b ? -1 : 1
  39. }
  40. //中序遍历,注意递归调用的顺序
  41. middleMap(callback) {
  42. this.middleMapNode(this.root, callback)
  43. }
  44. middleMapNode(node, callback) {
  45. if (node != null) {
  46. this.middleMapNode(node.left, callback)
  47. callback(node.key)
  48. this.middleMapNode(node.right, callback)
  49. }
  50. }
  51. //先序遍历
  52. prevMap(callback) {
  53. this.prevMapNode(this.root, callback)
  54. }
  55. prevMapNode(node, callback) {
  56. if (node != null) {
  57. callback(node.key)
  58. this.prevMapNode(node.left, callback)
  59. this.prevMapNode(node.right, callback)
  60. }
  61. }
  62. //后序遍历
  63. behindMap(callback) {
  64. this.behindMapNode(this.root, callback)
  65. }
  66. behindMapNode(node, callback) {
  67. if (node != null) {
  68. this.behindMapNode(node.left, callback)
  69. this.behindMapNode(node.right, callback)
  70. callback(node.key)
  71. }
  72. }
  73. min() {
  74. return this.minNode(this.root)
  75. }
  76. minNode(node) {
  77. let current = node
  78. while (current != null && current.left != null) {
  79. current = current.left
  80. }
  81. return current.key
  82. }
  83. max() {
  84. return this.maxNode(this.root)
  85. }
  86. maxNode(node) {
  87. let current = node
  88. while (current != null && current.right != null) {
  89. current = current.right
  90. }
  91. return current.key
  92. }
  93. search(key) {
  94. return this.searchNode(this.root, key)
  95. }
  96. searchNode(node, key) {
  97. if (node === null) {
  98. return false
  99. }
  100. if (this.compareFn(key, node.key) === -1) {
  101. return this.searchNode(node.left, key)
  102. } else if (this.compareFn(key, node.key) === 1) {
  103. return this.searchNode(node.right, key)
  104. } else {
  105. return true
  106. }
  107. }
  108. remove(key) {
  109. this.removeNode(this.root, key)
  110. }
  111. removeNode(node, key) {
  112. if (node == null) {
  113. return null
  114. }
  115. if (this.compareFn(key, node.key) === -1) {
  116. node.left = this.removeNode(node.left, key)
  117. console.log(node);
  118. return node
  119. } else if (this.compareFn(key, node.key) === 1) {
  120. node.right = this.removeNode(node.right, key)
  121. console.log(node);
  122. return node
  123. } else {
  124. if (node.left == null && node.right == null) {
  125. node = null
  126. return node
  127. }
  128. if (node.left == null) {
  129. node = node.right
  130. return node
  131. } else if (node.right == null) {
  132. node = node.left
  133. return node
  134. }
  135. const target = this.minNode(node.right)
  136. node.key = target.key
  137. node.right = this.removeNode(node.right, target.key)
  138. return node
  139. }
  140. }
  141. }
  142. const tree = new BST()
  143. tree.insert(6)
  144. tree.insert(4)
  145. tree.insert(8)
  146. tree.insert(3)
  147. tree.insert(5)
  148. tree.middleMap((value) => console.log(value)) //3 4 5 6 8
  149. tree.prevMap((value) => console.log(value)) //6 4 3 5 8
  150. tree.behindMap((value) => console.log(value)) //3 5 4 8 6

(8)>>二叉堆

二叉堆是一种特殊的二叉树,有两种特性

  • 它是一棵完全二叉树,表示树的每一层都有左侧和右侧子节点(除了最后一层的叶节点), 并且最后一层的叶节点尽可能都是左侧子节点,这叫作结构特性。

  • 二叉堆不是最小堆就是最大堆。最小堆允许你快速导出树的最小值,最大堆允许你快速 导出树的最大值。所有的节点都大于等于(最大堆)或小于等于(最小堆)每个它的子 节点。这叫作堆特性。

1.最小堆

  1. //把二叉堆的数据按数组的格式存储起来
  2. class minHeap {
  3. heap = []
  4. //根据现有节点计算左子节点
  5. getLeftSonIndex(index) {
  6. return 2 * index + 1
  7. }
  8. getRightSonIndex(index) {
  9. return 2 * index + 2
  10. }
  11. getParentIndex(index) {
  12. if (index === 0) {
  13. return undefined
  14. } else {
  15. return Math.floor((index - 1) / 2)
  16. }
  17. }
  18. insert(value) {
  19. if (value != null) {
  20. this.heap.push(value)
  21. this.shiftUp(this.heap.length - 1)
  22. return true
  23. }
  24. return false
  25. }
  26. shiftUp(index) {
  27. let parent = this.getParentIndex(index)
  28. while (index > 0 && this.compareFn(this.heap[parent], this.heap[index]) === 1) {
  29. this.swap(this.heap, parent, index)
  30. index = parent
  31. parent = this.getParentIndex(index)
  32. }
  33. }
  34. compareFn(a, b) {
  35. if (a === b) {
  36. return 0
  37. }
  38. return a < b ? -1 : 1
  39. }
  40. swap(arr, a, b) {
  41. let temp = arr[a]
  42. arr[a] = arr[b]
  43. arr[b] = temp
  44. }
  45. size() {
  46. return this.heap.length
  47. }
  48. isEmpty() {
  49. return this.size() === 0
  50. }
  51. findMinimun() {
  52. return this.heap[0]
  53. }
  54. //去除根节点及后面的操作
  55. extractFirst() {
  56. if (this.isEmpty()) {
  57. return undefined
  58. }
  59. if (this.size() === 1) {
  60. return this.heap.shift()
  61. }
  62. const removedValue = this.heap[0]
  63. this.heap[0] = this.heap.pop()
  64. this.shiftDown(0)
  65. return removedValue
  66. }
  67. shiftDown(index) {
  68. let current = index
  69. let left = this.getLeftSonIndex(index)
  70. let right = this.getRightSonIndex(index)
  71. if (left < this.size() && this.compareFn(this.heap[current], this.heap[left]) === 1) {
  72. current = left
  73. }
  74. if (right < this.size() && this.compareFn(this.heap[current], this.heap[right]) === 1) {
  75. current = right
  76. }
  77. if (index !== current) {
  78. this.swap(this.heap, index, current)
  79. this.shiftDown(current)
  80. }
  81. }
  82. }

2.最大堆

可以偷个懒,直接继承并随便改写一下比较两数大小的方法

  1. class maxHeap extends minHeap {
  2. constructor() {
  3. super()
  4. }
  5. compareFn(a, b) {
  6. if (a === b) {
  7. return 0
  8. }
  9. return a > b ? -1 : 1
  10. }
  11. }

三.总结

        数据结构是个神奇的东西,它充斥在代码中,却仿佛离我们那么遥远,学习数据结构不仅是在面对不同的数据时要施加不同的策略,而且可以提高我们的代码阅读能力。当然了,对于数据结构的学习依然任重而道远,各位同仁加油吧,希望可以点赞收藏加关注嘿嘿。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/90454?site
推荐阅读
相关标签
  

闽ICP备14008679号