数据结构《学习笔记》——二叉树

这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战node

1、树的介绍

  • 树:n(n >= 0)个节点构成的有限集合
  • 对于任一棵非空树(n > 0),具有如下性质:
    • 树中有一个称为**"根"**的特殊节点
    • 其他节点可分为m(m > 0)个互不相交的有限集T一、T二、...、Tm,其中每一个集合自己又是一棵树,称为原来树的**"子树"**

树的性质是不少的...数组

  • 节点的度:树的节点个数
  • 叶节点:度为0的节点(也称为叶子节点)
  • 节点的层次:规定根节点在1层,其它任一节点的层数是其父节点的层数加1

2、二叉树

二叉树的特性

  • 一个二叉树第i层的最大节点数为:2^(i-1), i>=1markdown

    好比第一层(根节点)为1个;第二层最多为2节点函数

  • 深度为k的二叉树有最大节点总数为:2^k-1, k>=1post

  • 对任何非空二叉树T,若n0表示叶节点(度为0)的个数,n2表示度为2的非叶节点个数,那么二者知足关系n0 = n2 + 1ui

a. 彻底二叉树

  • 除二叉树最后一层外,其余各层的节点数都达到最大个数this

  • 且最后一层从左向右达到叶节点连续存在,只缺右侧若干节点spa

  • 下面的图从左到右看,E有右子节点可是D没有,因此不是彻底二叉树。若是给D加一个右子节点,那么它就是彻底二叉树prototype

彻底二叉树.png

b. 完美二叉树(满二叉树)

  • 在二叉树中,除了最下一层的叶节点外,每层节点都有2个子节点,就构成了满二叉树

满二叉树.png

二叉树的存储方式能够是数组也能够是链表,可是咱们通常用链表。由于使用数组的是否若是该二叉树不是满二叉树,会形成不少空间的浪费。指针

3、二叉搜索树

  • 二叉搜索树是一颗二叉树,能够为空;若是不为空,知足如下性质:
  • 非空子树的全部键值小于其根节点的键值
  • 非空子树的全部键值大于其根节点的键值
  • 左、右子树自己也都是二叉搜索树

二叉搜索树初始化

  • 首先,二叉搜索树有一个根节点,初始化让这个根节点指向空;this.root = null;

  • 其次,二叉搜索树的每个节点包含3个元素,左子节点、右子节点和键值;初始状态下指针指向空

    function Node() {
        this.key = key;
        this.left = null;
        this.right = null;
    }
    复制代码

4、二叉搜索树常见方法

  • insert(key):向树中插入一个新的键
  • search(key):在树中查找一个键,若是节点存在,则返回true;若是不存在,返回false
  • inOrderTraverse:经过中序遍历方式遍历全部节点
  • preOrderTraverse:经过先序遍历方式遍历全部节点
  • postOrderTraverse:经过后序遍历方式遍历全部节点
  • min:返回树中的最小的值/键
  • max:返回树中的最大的值/键
  • remove(key):从树中移除某个键

1. insert方法

  1. 根据key建立节点

  2. 判断根节点是否有值;若是根节点为null时直接插入,不然进行下一步操做

    BinarySearchTree.prototype.insert = function (key) {
        var newNode = new Node(key);
        if (this.root == null) {
            this.root = newNode;
        } else {
            this.insertNode(this.root, newNode);
        }
    }
    复制代码
  3. 在搜索树进行插入操做的时候,若是插入的值大于根节点,那么就要把该节点插到根节点的右子节点;若是此时根节点已经有右子节点了,那就应该继续让这个要插入的节点的值和该右子节点进行比较插入;这个过程其实就是在套娃,一直到这个左或者右节点为空的时候就中止,因此这里用递归来实现,用另外一个函数来进行对节点的插入。

    BinarySearchTree.prototype.insertNode = function (node, newNode) {
        if (newNode.key < node.key) {
            if (node.left == null) {
                node.left = newNode;
            } else {
                this.insertNode(node.left, newNode);
            }
        } else {
            if (node.right == null) {
                node.right = newNode;
            } else {
                this.insertNode(node.right, newNode);
            }
        }
    }
    复制代码

2. 遍历二叉搜索树

因为树的结构是比较特殊的,在遍历节点的时候咱们每每都是采用递归的方法来实现。

a. 先序遍历

  • 访问根节点
  • 先先序遍历其左子树,再先序遍历右子树
BinarySearchTree.prototype.preOrderTraversalNode = function (node, handler) {
    if (node != null) {
        handler(node.key);
        this.preOrderTraversalNode(node.left, handler);
        // 3. 处理通过节点的右子节点
        this.preOrderTraversalNode(node.right, handler);
    }
}
//--------
var resultString = ''
tree.preOrderTraversal(function (key) {
    resultString += key + ' ';
})
复制代码

这里的handler是一个处理结点的回调函数,便于咱们看遍历的结果;在先序遍历中,咱们要先遍历全部的左子节点,再处理通过节点的右子节点;好比下面这张图:

这里用的递归思路仍是有一点复杂的,是一个个函数的嵌套,遍历结束以后一个个跳出来的,须要花一点时间来理解。

遍历7的左节点到5的时候,先遍历3,3是叶节点,继续遍历6(此时的node是5),遍历完5的左右节点以后,就继续遍历7的右子节点。

先序遍历.png

b. 中序遍历

  • 中序遍历其左子树
  • 访问根节点
  • 中序遍历右子树
if (node != null) {
    this.inOrderTraversalNode(node.left, handler);
    handler(node.key);
    this.inOrderTraversalNode(node.right, handler);
}
复制代码

c. 后序遍历

  • 后序遍历其左子树,再后序遍历其右子树
  • 访问根节点
if (node != null) {
    this.inOrderTraversalNode(node.left, handler);
    this.inOrderTraversalNode(node.right, handler);
    handler(node.key);
}
复制代码

理解了先序遍历以后,中序遍历和后序遍历都是用递归的方式实现,不一样之处在于什么时候来处理节点。

3. 最大值&最小值

最大值和最小值在数中是很容易找的。最左边的子节点(左下)就是最小值;最右边的子节点(右下)就是最大值。这里演示查找最大值。

  • 获取根节点
  • 依次向右查找,直到节点为null
BinarySearchTree.prototype.max = function () {
    var node = this.root;
    var key = null;
    while (node != null) {
        key = node.key
        node = node.right
    }
    return node.key
}
复制代码

在查找的时候,一直都找右节点,直到这个右节点的值为空的时候返回这个节点的键值。

4. 搜索特定的值

这个方法能够经过递归的方法实现,也能够经过循环来进行搜索

  • 递归;将查找值和当前节点的值进行对比,若是较小就往左子树找,若是较大就往右子树上查找,直到node.key == key时,返回true,若是通过这些步骤以后仍是找不到的话就返回false。

    BinarySearchTree.prototype.searchNode = function (node, key) {
        if (node == null) {
            return false;
        }
        if (node.key > key) {
            return this.searchNode(node.left, key);
        } else if (node.key < key) {
            return this.searchNode(node.right, key);
        } else {
            return true;
        }
        return false;
    }
    复制代码
  • 循环 先获取根节点,再循环搜索key

    循环的话会比较好理解一点,就是键值比较,决定往左子树仍是右子树遍历,直到找到目标值。

    var node = this.root;
    while (node != null) {
        if (node.key > key) {
            node = node.left;
        } else if (node.key < key) {
            node = node.right;
        } else {
            return true;
        }
    }
    return false;
    复制代码

删除操做是最复杂的,那就放在下一篇文章吧!今天学不下去了aaa...codewhy老师真的讲的好棒!

相关文章
相关标签/搜索