欢迎关注我的 个人主页 && 个人博客 && 个人知识库 && 微信公众号“前端自习课”

本周练习内容:数据结构与算法 —— Tree

这些都是数据结构与算法,一部分方法是团队其他成员实现的,一部分我自己做的,有什么其他实现方法或错误,欢迎各位大佬指点,感谢。

一、什么是树?

1.树有什么特点,什么是二叉树和二叉搜索树(BST: Binary Search Tree)?
2.生活中常见的例子有哪些?


解析:

  1. 树有什么特点,什么是二叉树和二叉搜索树:
  • 是一种非线性的数据结构,以分层方式存储数据,用来表示有层级关系的数据
  • 每棵树至多只有一个根结点根结点会有很多子节点,每个子节点只有一个父结点
  • 父结点子节点是相对的。
  1. 生活中的例子:
    如:家谱、公司组织架构图。

二、请实现二叉搜索树(BST),并实现以下方法:

  • insert(key):向树中插入一个新的键;
  • search(key):树中查找一个键,如果节点存在返回true,不存在返回false;
  • min():返回树中最小的值/键;
  • max():返回树中最大的值/键;
  • remove(key):移除某个键;

提示:所谓的键对应于之前章节所学的节点(Node)

  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. /**
  13. * 插入一个节点
  14. * @param {*} node 插入的位置节点
  15. * @param {*} newNode 插入的节点
  16. */
  17. insertNode (node, newNode){
  18. if(newNode.key < node.key){
  19. if(node.left === null && node.right === null){
  20. node.left = newNode
  21. }else if(node.left !== null && node.right === null){
  22. node.right = newNode
  23. }else{
  24. this.insertNode(node.left, newNode)
  25. }
  26. }else{
  27. if(node.left === null && node.right === null){
  28. node.left = newNode
  29. }else if(node.left !== null && node.right === null){
  30. node.right = newNode
  31. }else{
  32. this.insertNode(node.right, newNode)
  33. }
  34. }
  35. }
  36. /**
  37. * 插入操作
  38. * @param {*} key
  39. */
  40. insert (key){
  41. let newNode = new Node(key)
  42. if(this.root === null){
  43. this.root = newNode
  44. }else{
  45. this.insertNode(this.root, newNode)
  46. }
  47. }
  48. searchNode (node, key){
  49. if(node === null) return false
  50. if(key < node.key){
  51. return this.searchNode(node.left, key)
  52. }else if(key > node.key){
  53. return this.searchNode(node.right, key)
  54. }else{
  55. return true
  56. }
  57. }
  58. /**
  59. * 搜索操作
  60. * @param {*} key
  61. */
  62. search (key){
  63. return this.searchNode(this.root, key)
  64. }
  65. /**
  66. * 最小值的节点
  67. */
  68. min (){
  69. let node = this.root
  70. if(node === null) return null
  71. while(node && node.left !== null){
  72. node = node.left
  73. }
  74. return node.key
  75. }
  76. /**
  77. * 最大值的节点
  78. */
  79. max (){
  80. let node = this.root
  81. if(node === null) return null
  82. while(node && node.right !== null){
  83. node = node.right
  84. }
  85. return node.key
  86. }
  87. /**
  88. * 找到最小节点
  89. * @param {*} node
  90. */
  91. findMinNode (node){
  92. if(node === null) return null
  93. while(node && node.left !== null){
  94. node = node.left
  95. }
  96. return node
  97. }
  98. /**
  99. * 删除一个节点
  100. * @param {*} node
  101. * @param {*} key
  102. */
  103. removeNode (node, key){
  104. if(node === null) return null
  105. if(key < node.key){
  106. node.left = this.removeNode(node.left, key)
  107. return node
  108. }else if(key > node.key){
  109. node.right = this.removeNode(node.right, key)
  110. return node
  111. }else{
  112. // 1.叶节点
  113. if(node.left === null && node.right === null){
  114. node = null
  115. return node
  116. }
  117. // 2.只有一个子节点
  118. if(node.left === null){
  119. node = node.right
  120. return node
  121. }else if(node.right === null){
  122. node = node.left
  123. }
  124. // 3.有两个子节点
  125. let curNode = this.findMinNode(node.right)
  126. node.key = curNode.key
  127. node.right = this.removeNode(node.right, curNode.key)
  128. return node
  129. }
  130. }
  131. /**
  132. * 删除一个节点
  133. * @param {*} key
  134. */
  135. remove (key){
  136. if(this.root === null) return null
  137. this.root = this.removeNode(this.root, key)
  138. }
  139. }

三、基于题二实现二叉搜索树扩展以下方法:

  • preOrderTraverse(): 通过先序遍历方式遍历所有节点;
  • inOrderTraverse(): 通过中序遍历方式遍历所有节点;
  • postOrderTraverse(): 通过后序遍历方式遍历所有节点;

提示:

  • 先序:先访问根节点,然后以同样方式访问左子树和右子树;(根==>左==>右)

输出 =》 11 7 5 3 6 9 8 10 15 13 12 14 20 18 25
4.Tree - 图1

  • 中序:先访问左子树,再访问根节点,最后访问右字数;以升序访问所有节点;(左==>根==>右)

输出 =》 3 5 6 7 8 9 10 11 12 13 14 15 18 20 25

4.Tree - 图2

  • 后序:先访问叶子节点,从左子树到右子树,再到根节点。(左==>右==>根)

输出 =》 3 6 5 8 10 9 7 12 14 13 18 25 20 15 11

4.Tree - 图3


解析:

  1. // 1. 先序
  2. BST.prototype.preOrderTraverseNode = function(node, callback){
  3. if(node !== null){
  4. callback(node.key)
  5. this.preOrderTraverseNode(node.left, callback)
  6. this.preOrderTraverseNode(node.right, callback)
  7. }
  8. }
  9. BST.prototype.preOrderTraverse = function(callback){
  10. this.preOrderTraverseNode(this.root, callback)
  11. }
  12. // 2. 中序
  13. BST.prototype.inOrderTraverseNode = function(node, callback){
  14. if(node !== null){
  15. this.inOrderTraverseNode(node.left, callback)
  16. callback(node.key)
  17. this.inOrderTraverseNode(node.right, callback)
  18. }
  19. }
  20. BST.prototype.inOrderTraverse = function(callback){
  21. this.inOrderTraverseNode(this.root, callback)
  22. }
  23. // 3. 后序
  24. BST.prototype.postOrderTraverseNode = function(node, callback){
  25. if(node !== null){
  26. this.postOrderTraverseNode(node.left, callback)
  27. this.postOrderTraverseNode(node.right, callback)
  28. callback(node.key)
  29. }
  30. }
  31. BST.prototype.postOrderTraverse = function(callback){
  32. this.postOrderTraverseNode(this.root, callback)
  33. }

四、请实现从上往下打印二叉树

给定的二叉树为:[3, 9 , 20, null, null, 15, 7]

  1. 3
  2. / \
  3. 9 20
  4. / \
  5. 15 7

请实现一个 printLevelOrder 方法,输出以下结果:

  1. [
  2. [3],
  3. [9, 20],
  4. [15, 7]
  5. ]

来源:102.二叉树的层次遍历
解析:

  • 方法一:
  1. BST.prototype.printLevelOrder = function (root, arr = [], i = 0){
  2. if (root && (root.key || root.key === 0)) {
  3. !arr[i] && (arr[i] = [])
  4. arr[i].push(root.key)
  5. i++
  6. root.left && this.printLevelOrder(root.left, arr, i)
  7. root.right && this.printLevelOrder(root.right, arr, i)
  8. }
  9. return arr
  10. }
  • 方法二:
  1. BST.prototype.printLevelOrder = function (){
  2. if(this.root === null) return []
  3. let result = [], queue = [this.root]
  4. while(true){
  5. let len = queue.length, arr = []
  6. while(len > 0){
  7. console.log(queue)
  8. let node = queue.shift()
  9. len -= 1
  10. arr.push(node.key)
  11. if(node.left !== null) queue.push(node.left)
  12. if(node.right !== null) queue.push(node.right)
  13. }
  14. if(arr.length === 0) return result
  15. result.push([...arr])
  16. }
  17. }

五、给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

  1. 输入:
  2. 2
  3. / \
  4. 1 3
  5. 输出: true

示例 2:

  1. 输入:
  2. 5
  3. / \
  4. 1 4
  5. / \
  6. 3 6
  7. 输出: false
  8. 解释: 输入为: [5,1,4,null,null,3,6]。
  9. 根节点的值为 5 ,但是其右子节点值为 4

代码实现:

  1. /**
  2. * 二叉树节点定义
  3. */
  4. function TreeNode(val) {
  5. this.val = val;
  6. this.left = this.right = null;
  7. }
  8. /**
  9. - @param {TreeNode} root
  10. - @return {boolean}
  11. */
  12. function isValidBST(root) {};

来源:99.验证二叉搜索树
解析:

  1. function isValidBST(root) {
  2. let arr = []
  3. function inOrderTraverse(node){
  4. if(node === null) return;
  5. node.left && inOrderTraverse(node.left);
  6. arr.push(node.val);
  7. node.right && inOrderTraverse(node.right);
  8. }
  9. inOrderTraverse(root)
  10. for(let i = 0; i < arr.length - 1; i++){
  11. if(arr[i] >= arr[i+1]) return false
  12. }
  13. return true
  14. };
Author 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推荐 https://github.com/pingan8787/Leo_Reading/issues
ES小册 js.pingan8787.com

微信公众号

4.Tree - 图4