博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
二叉树的实现
阅读量:5797 次
发布时间:2019-06-18

本文共 5017 字,大约阅读时间需要 16 分钟。

概念

二叉树(Binary Tree)是另一种树型结构,它的特点是每个结点至多只有两棵子树(即二叉树中不存在度大于 2 的结点),并且,二叉树的子树有左右之分(其次序不能任意颠倒。)

性质

  • 二叉树的第 i 层上最多有 2 的(i-1)方个节点。(i>=1)
  • 深度为 k 的树最多有 2 的 k 次方-1 个节点。(k>=1)
  • 对任何一棵二叉树 T,如果其终端结点数为 n0,度为 2 的结点数为 n2,则 n0 = n2 + 1;
    _ 一棵深度为 k 且有 2 的 k 次方-1 个结点的二叉树称为满二叉树
    _ 深度为 k 的,有 n 个结点的二叉树,当且仅当其每一个结点都与深度为 k 的满二叉树中编号从 1 至 n 的结点一一对应时,称之为完全二叉树
    <!-- more -->

初始化二叉树结构

// 创建节点class Node {  constructor(key) {    this.key = key    this.left = null    this.right = null  }}
// 创建二叉树格式class BinaryTree {  constructor(...args) {    this.root = null    // 初始化依次插入数据    args.forEach(key => {      this.insert(key)    })  }  // 实例化  static create(args) {    return new BinaryTree(...args)  }}

新增方法

思路:判断插入的节点和当前节点,若大于当前节点,去右子树插入,否则左子树插入。

insert(key) {  const newNode = new Node(key);  const insertNode = function(node, newNode) {    if (newNode.key < node.key) {      // 如果新节点的值小于老节点的值      if (node.left === null) {        // 如果老节点没有左孩子        node.left = newNode;      } else {        // 如果老节点有左孩子,那么讲数据插入到老节点的左孩子        insertNode(node.left, newNode);      }    } else {      // 如果新节点的值大于老节点      if (node.right === null) {        node.right = newNode;      } else {        insertNode(node.right, newNode);      }    }  };  if (this.root === null) {    // 如果root不存在,将newNode设为根节点    this.root = newNode  } else {    insertNode(this.root, newNode)  }}

调用形式

const nodes = [8, 3, 10, 1, 6, 14, 4, 7, 13]const binaryTree = BinaryTree.create(nodes)binaryTree.insert(55)console.log(binaryTree.root)

遍历方法

二叉树的遍历(traversing binary tree)是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。

前序遍历(DLR)

首先访问根结点,然后遍历左子树,最后遍历右子树。简记根-左-右。
  • 用途:用于复制一颗二叉树
  • 算法思路
    若二叉树为空,则遍历结束;否则 1. 访问根结点 2. 先序遍历左子树(递归调用本算法) 3. 先序遍历右子树(递归调用本算法)
// 前序遍历preOrderTraverse () {    const preOrderTraverse = node => {      if (node !== null) {        console.log(node.key)        preOrderTraverse(node.left)        preOrderTraverse(node.right)      }    }    preOrderTraverse(this.root)}

中序遍历(LDR)

首先遍历左子树,然后访问根结点,最后遍历右子树。简记左-根-右。
  • 用途:用于从小到大排序二叉树
  • 算法思路
    若二叉树为空,则遍历结束;否则 1. 中序遍历左子树(递归调用本算法) 2. 访问根结点 3. 中序遍历右子树(递归调用本算法)
//使用递归方式实现中序遍历inOrderTraverse () {    const inOrderTraverseNode = node => {      if (node !== null) {        // 如果当前节点非空,则访问左子树        inOrderTraverseNode(node.left)        // 直到访问到最底部的左子树才进入callback,每个节点都会有callback        console.log(node.key)        // 此时已经是最底部的左子树        inOrderTraverseNode(node.right)      }      // 解释:首先进入8,发现左边有3,执行左边遍历,遍历完后执行3的回调,在执行3的右边的回调,    }    inOrderTraverseNode(this.root)}

后序遍历(LRD)

首先遍历左子树,然后遍历右子树,最后访问根结点。简记左-右-根。
  • 算法思路
    若二叉树为空,则遍历结束;否则 1. 后序遍历左子树(递归调用本算法); 2. 后序遍历右子树(递归调用本算法) ; 3. 访问根结点 。
postOrderTraverse () {    const postOrderTraverse = node => {      if (node !== null) {        postOrderTraverse(node.left)        postOrderTraverse(node.right)        console.log(node.key)      }    }    postOrderTraverse(this.root)  }

查找算法

查找最大值

思路:传入二叉树,寻找右子树,直到找到不存在右子树的节点。

// 查找最大值max () {    const maxNode = node => {      if (node !== null) {        if (node.right) {          return maxNode(node.right)        } else {          return node.key        }      }    }    return maxNode(this.root)}

查找最小值

思路:传入二叉树,寻找左子树,直到找到不存在左子树的节点。

// 查找最小值min () {    const minNode = node => {      if (node !== null) {        if (node.left) {          return minNode(node.left)        } else {          return node.key        }      }    }    return minNode(this.root)}

查找任意值

思路:根据传入的 key 与当前节点比较,如果大于当前 key 则去右子树查找,否则去左子树查找。

// 查找指定值search (key) {  const searchNode = function(node, key) {    if (node === null) {      return false    }    if (key < node.key) {      return searchNode(node.left, key)    } else if (key > node.key) {      return searchNode(node.right, key)    } else {      return true    }  }  return searchNode(this.root, key)}

删除算法

思路:如果删除的 key 小于当前节点,去左子树种查找,否则去右子树查找。找到节点后,判断是否存在子节点,若不存在,直接删除,若只存在左节点或者右节点,将当前节点替换为它的子节点。若左右节点都存在,将右节点中的最小值(左子树)移除并替换为删除的节点位置(为了满足二叉树的左子树小于右子树)

remove (key) {  // 用于查找最小节点  const findMinNode = node => {    if (node) {      // 如果node的左孩子存在      while (node && node.left !== null) {        // 将node设为node的左孩子再次进入循环        node = node.left      }      // 直到返回没有左孩子的node      return node    }  }  const removeNode = (node, key) => {    if (node === null) {      return false    }    if (key < node.key) {      // 当前node大于删除key,去左孩子中查找      node.left = removeNode(node.left, key)      return node    } else if (key > node.key) {      // 当前node小于删除key,去右孩子中查找      node.right = removeNode(node.right, key)    } else {      // key和当前node相等      if (node.left === null && node.right === null) {        node = null        return node      }      // 任意一边没有值,取另一边      if (node.left === null) {        node = node.right        return node      } else if (node.right === null) {        node = node.left        return node      }      // 同时存在左孩子和右孩子      // 找出右边的最小值      const aux = findMinNode(node.right)      // 将最小值替换为删除的key      node.key = aux.key      // 在右孩子中删除最小值      node.right = removeNode(node.right, aux.key)      return node    }  }  const result = removeNode(this.root, key)  console.log(result)}

转载地址:http://ypsfx.baihongyu.com/

你可能感兴趣的文章
聊一聊log4j2配置文件log4j2.xml
查看>>
NeHe OpenGL教程 第七课:光照和键盘
查看>>
修改上一篇文章的node.js代码,支持默认页及支持中文
查看>>
Php实现版本比较接口
查看>>
删除设备和驱动器中软件图标
查看>>
Android studio开多个窗口引起的问题
查看>>
第四章 TCP粘包/拆包问题的解决之道---4.1---
查看>>
RedisRepository分享和纠错
查看>>
html语言
查看>>
Unity接入谷歌支付
查看>>
laravel 使用 vue (gulp)
查看>>
QT 信号槽connect中解决自定义数据类型或数组作为函数参数的问题——QT qRegisterMetaType 注册MetaType——关键:注册自定义数据类型或QMap等容器类...
查看>>
HTTP之二 http 301 和 302的区别
查看>>
从源码看集合ArrayList
查看>>
Gephi
查看>>
git 入门宝典
查看>>
Android NDK开发之旅3 C语言 指针
查看>>
网络框架 Retrofit(二)
查看>>
Masonry 快速修炼手册,带你打怪升级
查看>>
LeetCode之Score After Flipping Matrix(Kotlin)
查看>>