[前端] JavaScript二叉树及各种遍历算法详情

1998 0
Honkers 2022-10-21 15:46:09 | 显示全部楼层 |阅读模式
目录

    什么是二叉树
      满二叉树完全二叉树
    二叉树的存储
      数组存储链表存储
    与二叉树相关的算法
      深度优先遍历广度优先遍历先序遍历中序遍历后序遍历


前言:
上一篇文章中介绍了树的概念、深度优先遍历和广度优先遍历,这篇文章我们来学习一个特殊的树——二叉树。

什么是二叉树

二叉树是每个节点最多只能有两个子节点的树,如下图所示:


一个二叉树具有以下几个特质:
    第i层的节点最有只有2^(i-1)个;如果这颗二叉树的深度为k,那二叉树最多有2^k-1个节点;在一个非空的二叉树中,若使用n0表示叶子节点的个数,n2是度为2的非叶子节点的个数,那么两者满足关系n0 = n2 + 1。

满二叉树

如果在一个二叉树中,除了叶子节点,其余的节点的每个度都是2,则说明该二叉树是一个满二叉树
如下图所示:


满二叉树除了满足普通二叉树特质,还具有如下几个特质:
    满二叉树的的第n层具有2^(n-1)个节点;深度为k的满二叉树一定存在2^k-1个节点,叶子节点的个数为2^(k-1);具有n个节点的满二叉树的深度为log_2^(n+1)。

完全二叉树

如果一个二叉树去掉最后一次层是满二叉树,且最后一次的节点是依次从左到右分布的,则这个二叉树是一个完全二叉树,
如下图所示:



二叉树的存储

存储二叉树的常见方式分为两种,一种是使用数组存储,另一种使用链表存储。

数组存储

使用数组存储二叉树,如果遇到完全二叉树,存储顺序从上到下,从左到右,如下图所示:


如果是一个非完全二叉树,如下图所示:


需要先将其转换为完全二叉树,然后在进行存储,如下图所示:


可以很明显的看到存储空间的浪费。

链表存储

使用链表存储通常将二叉树中的分为3个部分,如下图:


这三个部分依次是左子树的引用,该节点包含的数据,右子树的引用,存储方式如下图所示:



与二叉树相关的算法

以下算法中遍历用到的树如下
  1. // tree.js
  2. const bt = {
  3.   val: 'A',
  4.   left: {
  5.     val: 'B',
  6.     left: { val: 'D', left: null, right: null },
  7.     right: { val: 'E', left: null, right: null },
  8.   },
  9.   right: {
  10.     val: 'C',
  11.     left: {
  12.       val: 'F',
  13.       left: { val: 'H', left: null, right: null },
  14.       right: { val: 'I', left: null, right: null },
  15.     },
  16.     right: { val: 'G', left: null, right: null },
  17.   },
  18. }
  19. module.exports = bt
复制代码
深度优先遍历

二叉树的深度优先遍历与树的深度优先遍历思路一致,思路如下:
    访问根节点;访问根节点的left访问根节点的right重复执行第二三步
实现代码如下:
  1. const bt = {
  2.   val: 'A',
  3.   left: {
  4.     val: 'B',
  5.     left: { val: 'D', left: null, right: null },
  6.     right: { val: 'E', left: null, right: null },
  7.   },
  8.   right: {
  9.     val: 'C',
  10.     left: {
  11.       val: 'F',
  12.       left: { val: 'H', left: null, right: null },
  13.       right: { val: 'I', left: null, right: null },
  14.     },
  15.     right: { val: 'G', left: null, right: null },
  16.   },
  17. }
  18. function dfs(root) {
  19.   if (!root) return
  20.   console.log(root.val)
  21.   root.left && dfs(root.left)
  22.   root.right && dfs(root.right)
  23. }
  24. dfs(bt)
  25. /** 结果
  26. A B D E C F H I G
  27. */
复制代码
广度优先遍历

实现思路如下:
    创建队列,把根节点入队把对头出队并访问把队头的left和right依次入队重复执行2、3步,直到队列为空
实现代码如下:
  1. function bfs(root) {
  2.   if (!root) return
  3.   const queue = [root]
  4.   while (queue.length) {
  5.     const node = queue.shift()
  6.     console.log(node.val)
  7.     node.left && queue.push(node.left)
  8.     node.right && queue.push(node.right)
  9.   }
  10. }
  11. bfs(bt)
  12. /** 结果
  13. A B C D E F G H I
  14. */
复制代码
先序遍历

二叉树的先序遍历实现思想如下:
    访问根节点;对当前节点的左子树进行先序遍历;对当前节点的右子树进行先序遍历;
如下图所示:


递归方式实现如下:
  1. const bt = require('./tree')
  2. function preorder(root) {
  3.   if (!root) return
  4.   console.log(root.val)
  5.   preorder(root.left)
  6.   preorder(root.right)
  7. }
  8. preorder(bt)
  9. /** 结果
  10. A B D E C F H I G
  11. */
复制代码
迭代方式实现如下:
  1. // 非递归版
  2. function preorder(root) {
  3.   if (!root) return
  4.   // 定义一个栈,用于存储数据
  5.   const stack = [root]
  6.   while (stack.length) {
  7.     const node = stack.pop()
  8.     console.log(node.val)
  9.     /* 由于栈存在先入后出的特性,所以需要先入右子树才能保证先出左子树 */
  10.     node.right && stack.push(node.right)
  11.     node.left && stack.push(node.left)
  12.   }
  13. }
  14. preorder(bt)
  15. /** 结果
  16. A B D E C F H I G
  17. */
复制代码
中序遍历

二叉树的中序遍历实现思想如下:
    对当前节点的左子树进行中序遍历;访问根节点;对当前节点的右子树进行中序遍历;
如下图所示:


递归方式实现如下:
  1. const bt = require('./tree')
  2. // 递归版
  3. function inorder(root) {
  4.   if (!root) return
  5.   inorder(root.left)
  6.   console.log(root.val)
  7.   inorder(root.right)
  8. }
  9. inorder(bt)
  10. /** 结果
  11. D B E A H F I C G
  12. */
复制代码
迭代方式实现如下:
  1. // 非递归版
  2. function inorder(root) {
  3.   if (!root) return
  4.   const stack = []
  5.   // 定义一个指针
  6.   let p = root
  7.   // 如果栈中有数据或者p不是null,则继续遍历
  8.   while (stack.length || p) {
  9.     // 如果p存在则一致将p入栈并移动指针
  10.     while (p) {
  11.       // 将 p 入栈,并以移动指针
  12.       stack.push(p)
  13.       p = p.left
  14.     }
  15.     const node = stack.pop()
  16.     console.log(node.val)
  17.     p = node.right
  18.   }
  19. }
  20. inorder(bt)
  21. /** 结果
  22. D B E A H F I C G
  23. */
复制代码
后序遍历

二叉树的后序遍历实现思想如下:
    对当前节点的左子树进行后序遍历;对当前节点的右子树进行后序遍历;访问根节点;
如下图所示:


递归方式实现如下:
  1. const bt = require('./tree')
  2. // 递归版
  3. function postorder(root) {
  4.   if (!root) return
  5.   postorder(root.left)
  6.   postorder(root.right)
  7.   console.log(root.val)
  8. }
  9. postorder(bt)
  10. /** 结果
  11. D E B H I F G C A
  12. */
复制代码
迭代方式实现如下:
  1. // 非递归版
  2. function postorder(root) {
  3.   if (!root) return
  4.   const outputStack = []
  5.   const stack = [root]
  6.   while (stack.length) {
  7.     const node = stack.pop()
  8.     outputStack.push(node)
  9.     // 这里先入left需要保证left后出,在stack中后出,就是在outputStack栈中先出
  10.     node.left && stack.push(node.left)
  11.     node.right && stack.push(node.right)
  12.   }
  13.   while (outputStack.length) {
  14.     const node = outputStack.pop()
  15.     console.log(node.val)
  16.   }
  17. }
  18. postorder(bt)
  19. /** 结果
  20. D E B H I F G C A
  21. */
复制代码
到此这篇关于JavaScript二叉树及各种遍历算法详情的文章就介绍到这了,更多相关JavaScript二叉树内容请搜索中国红客联盟以前的文章或继续浏览下面的相关文章希望大家以后多多支持中国红客联盟!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

荣誉红客

关注
  • 4009
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行