博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
js数据结构和算法(7)-二叉树
阅读量:5914 次
发布时间:2019-06-19

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

7-二叉树(第10章)

树是计算机科学中经常用到的一种数据结构。树是一种非线性的数据结构,以分层的方式存储数据 。本章将研究一种特殊的树: 二叉查找树。

7.1 什么是树

树是一种数据结构,由一组以边线连接的节点组成。以下就是一颗普通的树

树的节点:一棵树最上面的节点称为根节点,如果一个节点下面连接多个节点,那么该节点称为父节点,它下面的节点称为子节点。一个节点可以有 0 个、 1 个或多个子节点。没有任何子节点的节点称为叶子节点。

树的遍历:沿着树的一组特定的边,可以从一个节点走到另外一个与它不直接相连的节点。从一个节点到另一个节点的这一组边称为路径,在图中用虚线表示。以某种特定顺序访问树中所有的节点称为树的遍历

树的深度:树可以分为几个层次,根节点是第 0 层,它的子节点是第 1 层,子节点的子节点是第 2层,以此类推。树中任何一层的节点可以都看做是子树的根,该子树包含根节点的子节点,子节点的子节点等。我们定义树的层数就是树的深度

7.2 二叉树

首先我们要了解下什么是二叉树,二叉树是一种特殊的树,它的子节点个数不超过两个。二叉查找树是在二叉树的基础上对树的结构进行了进一步的约束。

二叉查找树又名二叉排序树,有时候也叫二叉搜索树。它具有以下特征:

  • 若左子树不为空,则左子树上所有的结点的值均小于它根节点的值
  • 若右子树不为空,那么右子树上所有节点的值均大于它根节点的值
  • 左右子树也分别为二叉查找树
  • 没有键值相等的节点

7.3 实现一个二叉树

二叉树都是由节点组成的,所以我们要实现二叉树必须要先实现节点,一个节点通常包含三部分——数据、左子节点、右子节点。因此我们可以定义这样的一个node对象来代表我们的节点。

class Node {  constructor(data, left, right) {    this.data = data    this.left = left    this.right = right  }}复制代码

Node 对象既保存数据,也保存和其他节点的链接

现在我们可以创建一个类,用来表示二叉查找树,我们让类只包含一个数据成员:一个表示二叉查找树根节点的 Node 对象。该类的构造函数将根节点初始化为 null,以此创建一个空节点。

接下来我们来实现一个insert方法,用来向树中插入新的节点。实现这个方法的算法如下:

  1. 如果跟节点为null,则把根节点设置为要插入的节点
  2. 如果待插入节点保存的数据小于当前节点,则获取当前节点的左节点,如果左节点存在则与待插入的节点进行比较,重复步骤2。如果当前节点的左节点不存在,则直接把待插入的节点设置为当前节点的左节点
  3. 如果待插入节点保存的数据大于当前节点,则获取当前节点的右节点,如果右节点存在则与待插入的节点进行比较,重复步骤2。如果当前节点的右节点不存在,则直接把待插入的节点设置为当前节点的右节点

实现二叉树

class BST {  constructor() {    this.root = null  }  insert(data) {    let n = new Node(data, null, null)    if (this.root === null) {      this.root = n    } else {      var current = this.root      while (true) {        if (n.data < current.data) {          if (current.left === null) {            current.left = n            break          }          current = current.left        } else {          if (current.right === null) {            current.right = n            break          }          current = current.right        }      }    }  }}复制代码

*验证二叉树

let bst = new BST()bst.insert(8)bst.insert(3)bst.insert(6)bst.insert(4)bst.insert(9)bst.insert(11)bst.insert(2)bst.insert(5)bst.insert(7)console.log('bst:',bst)复制代码

真实的打印结果很难显示在博客里,画成图的话如下:

实现是正确的,说明我们的二叉树程序没有问题

7.4 二叉树的遍历

二叉树遍历常用的有以下三种。

  • 前序遍历:先访问根->遍历左子树->遍历右子树
  • 中序遍历:遍历左子树->访问根->遍历右子树
  • 后序遍历:遍历左子树->遍历右子树->访问根

遍历的前中后顺序其实是指访问根节点的顺序

前序遍历:

// 前序遍历  preOrder(node) {    if (node != null) {      console.log(node.data)      this.preOrder(node.left)      this.preOrder(node.right)    }  }复制代码

中序遍历:

// 中序遍历  inOrder(node) {    if (node != null) {      this.inOrder(node.left)      console.log(node.data)      this.inOrder(node.right)    }  }复制代码

后序遍历

// 后序遍历  postOrder(node) {    if (node !== null) {      this.postOrder(node.left)      this.postOrder(node.right)      console.log(node.data)    }  }复制代码

**遍历的方法使用的是递归,非常绕,需要多理解理解

完整代码如下

class Node {  constructor(data, left, right) {    this.data = data    this.left = left    this.right = right  }}class BST {  constructor() {    this.root = null  }  insert(data) {    let n = new Node(data, null, null)    if (this.root === null) {      this.root = n    } else {      var current = this.root      while (true) {        if (n.data < current.data) {          if (current.left === null) {            current.left = n            break          }          current = current.left        } else {          if (current.right === null) {            current.right = n            break          }          current = current.right        }      }    }  }  // 前序遍历  preOrder(node) {    if (node != null) {      console.log(node.data)      this.preOrder(node.left)      this.preOrder(node.right)    }  }  // 中序遍历  inOrder(node) {    if (node != null) {      this.inOrder(node.left)      console.log(node.data)      this.inOrder(node.right)    }  }  // 后序遍历  postOrder(node) {    if (node !== null) {      this.postOrder(node.left)      this.postOrder(node.right)      console.log(node.data)    }  }}复制代码

测试:

let bst = new BST()bst.insert(8)bst.insert(3)bst.insert(6)bst.insert(4)bst.insert(9)bst.insert(11)bst.insert(2)bst.insert(5)bst.insert(7)bst.preOrder(bst.root) // 8 3 2 6 4 5 7 9 11bst.inOrder(bst.root) // 2 3 4 5 6 7 8 9 11bst.postOrder(bst.root) // 2 5 4 7 6 3 11 9 8复制代码

测试没有,说明我们的遍历是正确的。

7.5 二叉树查找

  • 查找给定的值

  • 查找最大值

  • 查找最小值

    // 查找最大是getMax() {  let node = this.root  while (true) {    if (node.right === null) {      console.log(node.data)      break    }    node = node.right  }}// 查找最小值getMin() {  let node = this.root  while (true) {    if (node.left === null) {      console.log(node.data)      break    }    node = node.left  }}getSomeNum(num) {  let node = this.root  while (node) {    if (num < node.data) {      node = node.left    } else if (num > node.data) {      node = node.right    } else if (num === node.data) {      console.log('node.data:', node.data)      break    }  }}复制代码

测试

bst.getMax() // 11bst.getMin() // 2bst.getSomeNum(9) // node.data: 9bst.getSomeNum(1) // 复制代码

7.6 从二叉树上删除节点

在二叉树上删除节点会比较复杂,如果要删除的节点没有子节点还好,如果要删除的节点有一个节点或者两个节点,都要考虑到这些节点的处理情况,这时就比较难以处理。

  • 如果待删除节点是叶子节点(没有子节点的节点),那么只需要将从父节点指向它的链接改为 null
  • 如果待删除节点只包含一个子节点,那么原本指向它的节点就得做些调整,使其指向它的子节点。
  • 最后,如果待删除节点包含两个子节点,正确的做法有两种:要么查找待删除节点左子树上的最大值,要么查找其右子树上的最小值。这里我们选择后一种方式。

程序如下:

getSmallest(ndoe) {    while (node.left !== null) {      node = node.left    }    return node  }  remove(data) {    this.root = this.removeNode(this.root, data)  }  // 如果要删除的节点是当前节点,则返回null,否则返回原节点,并按照规则,递归查找下一个节点是不是目标节点  removeNode(node, data) {    if (node === null) {      return null    }    if (data === node.data) {      if (node.left === null && node.right === null) {        return null      }      // 没有左子节点      if (node.left === null) {        return node.right      }      // 没有右子节点      if (node.right === null) {        return node.left      }      // 有两个字节点      let tempNode = this.getSmallest(node.right)      node.data = tempNode.data      node.right = this.removeNode(node.right, tempNode.data)      return node    } else if (data < node.data) {      node.left = this.removeNode(node.left, data)      return node    } else {      node.right = this.removeNode(node.right, data)      return node    }  }复制代码

测试

class Node {  constructor(data, left, right) {    this.data = data    this.left = left    this.right = right  }}class BST {  constructor() {    this.root = null  }  insert(data) {    let n = new Node(data, null, null)    if (this.root === null) {      this.root = n    } else {      var current = this.root      while (true) {        if (n.data < current.data) {          if (current.left === null) {            current.left = n            break          }          current = current.left        } else {          if (current.right === null) {            current.right = n            break          }          current = current.right        }      }    }  }  // 前序遍历  preOrder(node) {    if (node != null) {      console.log(node.data)      this.preOrder(node.left)      this.preOrder(node.right)    }  }  // 中序遍历  inOrder(node) {    if (node != null) {      this.inOrder(node.left)      console.log(node.data)      this.inOrder(node.right)    }  }  // 后序遍历  postOrder(node) {    if (node !== null) {      this.postOrder(node.left)      this.postOrder(node.right)      console.log(node.data)    }  }  // 查找最大是  getMax() {    let node = this.root    while (true) {      if (node.right === null) {        console.log(node.data)        break      }      node = node.right    }  }  // 查找最小值  getMin() {    let node = this.root    while (true) {      if (node.left === null) {        console.log(node.data)        break      }      node = node.left    }  }  getSomeNum(num) {    let node = this.root    while (node) {      if (num < node.data) {        node = node.left      } else if (num > node.data) {        node = node.right      } else if (num === node.data) {        console.log('node.data:', node.data)        break      }    }  }  getSmallest(ndoe) {    while (node.left !== null) {      node = node.left    }    return node  }  remove(data) {    this.root = this.removeNode(this.root, data)  }  // 如果要删除的节点是当前节点,则返回null,否则返回原节点,并按照规则,递归查找下一个节点是不是目标节点  removeNode(node, data) {    if (node === null) {      return null    }    if (data === node.data) {      if (node.left === null && node.right === null) {        return null      }      // 没有左子节点      if (node.left === null) {        return node.right      }      // 没有右子节点      if (node.right === null) {        return node.left      }      // 有两个字节点      let tempNode = this.getSmallest(node.right)      node.data = tempNode.data      node.right = this.removeNode(node.right, tempNode.data)      return node    } else if (data < node.data) {      node.left = this.removeNode(node.left, data)      return node    } else {      node.right = this.removeNode(node.right, data)      return node    }  }}let bst = new BST()bst.insert(8)bst.insert(3)bst.insert(6)bst.insert(4)bst.insert(9)bst.insert(11)bst.insert(2)bst.insert(5)bst.insert(7)bst.remove(9)bst.inOrder(bst.root)// 打印结果234567811复制代码

从打印结果看,需要删除的数据被删除了,bst能正常遍历,中序遍历顺序也正确,说明我们删除程序没有问题。自此关于二叉树的知识点就基本讲完了。有实际情况时根据实际情况再调整。

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

你可能感兴趣的文章
大数除法
查看>>
HTML5--本地存储Web Storage
查看>>
mysql5.7.10 的源码安装
查看>>
oracle 插入单引号
查看>>
from __future__ import print_function
查看>>
字长与平台无关的整型数据类型
查看>>
telnet端口问题
查看>>
【Web缓存机制概述】5 – Web App时代的缓存机制新思路
查看>>
Bootstrap CSS教程
查看>>
Oracle RAC环境下如何更新patch(Rolling Patch)
查看>>
异构平台同步(Mysql到Oracle)
查看>>
HDU_1524 A Chess Game (sg函数)
查看>>
poj3132
查看>>
【图文教程】Oracle数据库的表的导入导出详细截图说明
查看>>
Linux中epoll用法小结
查看>>
一款帮助你生成非常有趣的扇形扑克牌风格特效的jQuery插件-Baraja
查看>>
HTML5实践 -- 如何使用css3丰富我们的图片样式 - part2
查看>>
C#整合VS2010和NUnit
查看>>
eclipse连接远程hadoop集群开发时0700问题解决方案
查看>>
九、Null在Java中的精确表示
查看>>