JavaScript实现数据结构与算法(四)树结构

  • (四)树结构
  • 1. 树的相关概念
  • 1.1 树的基本概念
  • 1.1.1 树结构的优势
  • 1.1.2 树的术语
  • 1.1.3 树的表示方法
  • 1.2 二叉树
  • 1.2.1 二叉树的概念
  • 1.2.2 二叉树的定义
  • 1.2.3 二叉树的五种形态
  • 1.2.4 二叉树的特性
  • 1.2.5 特殊二叉树
  • 1.2.6 二叉树的存储
  • 2. 二叉搜索树(BST)
  • 2.1 概念
  • 2.1.1 定义
  • 2.1.2 特点
  • 2.1.3 优势
  • 2.1.4 缺陷
  • 2.1.5 非平衡树
  • 2.2 代码封装
  • 2.2.1 代码解析
  • 2.2.2 常见操作
  • 2.2.3 封装二叉树代码实现
  • 3. 树的遍历
  • 3.1 先序遍历
  • 3.1.1 遍历顺序
  • 3.1.2 代码实现
  • 3.1.3 测试代码
  • 3.2 中序遍历
  • 3.2.1 遍历顺序
  • 3.2.2 代码实现
  • 3.2.3 测试代码
  • 3.3 后序遍历
  • 3.3.1 遍历顺序
  • 3.3.2 代码实现
  • 3.3.3 测试代码
  • 4. 二叉搜索树
  • 4.1 最大值最小值
  • 4.1.1 解析
  • 4.1.2 代码实现
  • 4.1.3 测试代码
  • 4.2 查找特定的值
  • 4.2.1 代码解析
  • 4.2.2 代码实现
  • 4.2.3 代码测试
  • 4.3 删除节点
  • 4.3.1 概念解析
  • 4.3.2 代码实现
  • 4.3.3 测试代码
  • 5. 树的平衡性
  • 5.1 定义
  • 5.2 常见的平衡树
  • 5.2.1 AVL树
  • 5.2.2 红黑树
  • 6. 红黑树
  • 6.1 规则
  • 6.2 相对平衡
  • 6.3 变换
  • 6.3.1 变色
  • 6.3.2 旋转
  • 6.3.3 插入操作
  • 6.3.3.1 插入分析
  • 6.3.3.2 红黑树插入案例


(四)树结构

学习笔记:coderwhy的JavaScript实现数据结构与算法的课程

1. 树的相关概念

1.1 树的基本概念
1.1.1 树结构的优势

Nodejs 物联网_Nodejs 物联网

1.1.2 树的术语

Nodejs 物联网_数据结构_02


节点的度、树的度、叶节点、父节点、子节点、兄弟节点、路径和路劲长度

Nodejs 物联网_树结构_03

1.1.3 树的表示方法

(1)普通表示方法

Nodejs 物联网_数据结构_04


(2)儿子兄弟表示方法

Nodejs 物联网_算法_05


(3)儿子兄弟表示方法的旋转

Nodejs 物联网_Nodejs 物联网_06

1.2 二叉树
1.2.1 二叉树的概念

任何一棵树都可以用一棵二叉树来模拟出来。

Nodejs 物联网_树结构_07

1.2.2 二叉树的定义

Nodejs 物联网_数据结构_08

1.2.3 二叉树的五种形态

Nodejs 物联网_Nodejs 物联网_09

1.2.4 二叉树的特性

Nodejs 物联网_二叉树_10

1.2.5 特殊二叉树

(1)完美二叉树(满二叉树)

Nodejs 物联网_二叉树_11


(2) 完全二叉树

Nodejs 物联网_二叉树_12

1.2.6 二叉树的存储

最常用的方式是链表

数组一般用来表示完全二叉树

Nodejs 物联网_二叉树_13

2. 二叉搜索树(BST)

2.1 概念
2.1.1 定义

Nodejs 物联网_二叉树_14

2.1.2 特点

Nodejs 物联网_二叉树_15

2.1.3 优势

Nodejs 物联网_数据结构_16

2.1.4 缺陷

Nodejs 物联网_数据结构_17

2.1.5 非平衡树

Nodejs 物联网_算法_18

2.2 代码封装
2.2.1 代码解析

Nodejs 物联网_算法_19

2.2.2 常见操作

Nodejs 物联网_算法_20

2.2.3 封装二叉树代码实现
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>二叉搜索树</title>
</head>
<body>
<script>
    // 封装二叉搜索树
    function BinarySearchTree(){
        // 封装内部类
        function Node(key) {
            this.key = key;
            this.left = null;
            this.right = null;
        };

        // 属性
        this.root = null;

        // 方法
        // 插入数据:对外给用户调用的方法
        BinarySearchTree.prototype.insert = function (key) {
          // 1. 根据key创建节点
          var newNode = new Node(key);

          // 2. 判断根节点是否有值
            if (this.root == null){
                this.root = newNode;
            }else{
                this.insertNode(this.root, newNode);
            };
        };

        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);
                }
            }
        };
    };

    // 测试代码
    // 1. 创建BinarySearchTree
    var bst = new BinarySearchTree();

    // 2. 插入数据
    bst.insert(11);
    bst.insert(7);
    bst.insert(15);
    bst.insert(5);
    bst.insert(3);
    bst.insert(9);
    bst.insert(8);
    bst.insert(10);
    bst.insert(13);
    bst.insert(12);
    bst.insert(14);
    bst.insert(20);
    bst.insert(18);
    bst.insert(25);
    bst.insert(6);

</script>
</body>
</html>

3. 树的遍历

3.1 先序遍历
3.1.1 遍历顺序

Nodejs 物联网_二叉树_21

3.1.2 代码实现
// 树的遍历
        // 1. 先序遍历
        BinarySearchTree.prototype.preOrderTraversal = function (handler) {
            this.preOrderTraversalNode(this.root,handler);
        };
        BinarySearchTree.prototype.preOrderTraversalNode = function (node,handler) {
            if (node != null){
                // 1. 查找经过的节点
                handler(node.key);

                // 2. 查找经过节点的左子节点
                this.preOrderTraversalNode(node.left,handler);

                // 3. 查找经过节点的右子节点
                this.preOrderTraversalNode(node.right,handler);
            }
        };
3.1.3 测试代码
var resultString = "";
    bst.preOrderTraversal(function (key) {
        resultString += key + " "
    });
    alert(resultString);
3.2 中序遍历
3.2.1 遍历顺序

Nodejs 物联网_Nodejs 物联网_22

3.2.2 代码实现
// 2. 中序遍历
        BinarySearchTree.prototype.midOrderTraversal = function (handler) {
            this.midOrderTraversalNode(this.root,handler);
        };
        BinarySearchTree.prototype.midOrderTraversalNode = function (node,handler) {
            if (node != null){
                // 1. 查找左子树中的节点
                this.midOrderTraversalNode(node.left,handler);

                // 2. 查找经过的节点
                handler(node.key);

                // 3. 查找右子树中节点
                this.midOrderTraversalNode(node.right,handler);
            }
        };
3.2.3 测试代码
var resultString = "";
    bst.midOrderTraversal(function (key) {
        resultString += key + " "
    });
    alert(resultString);
3.3 后序遍历
3.3.1 遍历顺序

Nodejs 物联网_算法_23

3.3.2 代码实现
BinarySearchTree.prototype.postOrderTraversal = function (handler) {
            this.postOrderTraversalNode(this.root,handler);
        };
        BinarySearchTree.prototype.postOrderTraversalNode = function (node,handler) {
            if (node != null){
                // 1. 查找左子树中的节点
                this.postOrderTraversalNode(node.left,handler);
                // 2. 查找右子树中节点
                this.postOrderTraversalNode(node.right,handler);
                // 3. 查找经过的节点
                handler(node.key);

              }
        };
3.3.3 测试代码
var resultString = "";
    bst.postOrderTraversal(function (key) {
        resultString += key + " "
    });
    alert(resultString);

4. 二叉搜索树

4.1 最大值最小值
4.1.1 解析

Nodejs 物联网_Nodejs 物联网_24

4.1.2 代码实现
// 寻找最值
        BinarySearchTree.prototype.max = function () {
            // 1. 获取根节点
            var node = this.root;
            // 2. 依次向右不断的查找,直到节点为null
            var key = null;
            while (node != null){
                key = node.key;
                node = node.right;
            }
            return key;
        };
        BinarySearchTree.prototype.min = function () {
            // 1. 获取根节点
            var node = this.root;
            // 2. 依次向左不断的查找,直到节点为null
            var key = null;
            while (node != null){
                key = node.key;
                node = node.left;
            }
            return key;
        };
4.1.3 测试代码
alert(bst.max());
    alert(bst.min());
4.2 查找特定的值
4.2.1 代码解析

Nodejs 物联网_二叉树_25

4.2.2 代码实现
// 搜索某一个key
        BinarySearchTree.prototype.search = function (key) {
            // 1. 获取根节点
            var node = this.root;
            // 2. 循环搜索key
            while(node!=null){
                if (node.key == key){
                    return true;
                } else if(node.key > key ){
                    node = node.left;
                }else{
                    node = node.right;
                };
            };
            return false;
        };
4.2.3 代码测试
alert(bst.search(25));
    alert(bst.search(24));
    alert(bst.search(2));
4.3 删除节点
4.3.1 概念解析

Nodejs 物联网_树结构_26


Nodejs 物联网_数据结构_27


Nodejs 物联网_二叉树_28


Nodejs 物联网_数据结构_29


Nodejs 物联网_二叉树_30


前驱:该节点左子树的最大值

后继:该节点右子树的最小值

Nodejs 物联网_算法_31


Nodejs 物联网_Nodejs 物联网_32

4.3.2 代码实现
// 删除节点
        BinarySearchTree.prototype.remove = function (key) {
            // 1. 寻找要删除的节点
            // // 先判断树中是否有该值
            // if (!this.search(key)){
            //     return false;
            // }
            // 1.1 定义变量,保存一些信息
            var current = this.root;
            var parent = null;
            var isLeftChild = true; // 判断向左寻找的标志

            // 1.2 开始寻找删除的节点
            while (current.key != key) {
                parent = current;
                if (key < current.key) {
                    isLeftChild = true;
                    current = current.left;
                } else {
                    isLeftChild = false;
                    current = current.right;
                }
                if (current == null) {
                    return false;
                }
            }
            ;

            // 2. 根据对应的盈眶删除节点
            // 找到了current==key
            // 2.1 删除的节点是叶子节点(没有子节点)
            if (current.left == null && current.right == null) {
                if (current == this.root) {
                    this.root = null;
                } else {
                    if (isLeftChild) {
                        parent.left = null;
                    } else {
                        parent.right = null;
                    }
                    ;
                }
                ;
            }

            // 2.2 删除的节点有一个子节点
            else if (current.right == null) {
                if (current == this.root) {
                    this.root = left;
                } else if (isLeftChild) {
                    parent.left = current.left;

                } else {
                    parent.right = current.left;
            }
                ;
            } else if (current.left == null) {
                if (current == this.root) {
                    this.root = right;
                } else if (isLeftChild) {
                    parent.left = current.right;

                } else {
                    parent.right = current.right;
                }


            }
            // 2.3 删除的节点有两个子节点
            else{
                // 1. 获取后继节点
                var succssor = this.getSuccssor(current);

                // 2. 判断是否是根节点
                if (current == this.root){
                    this.root = succssor;
                }else if (isLeftChild) {
                    parent.left = succssor;
                }else{
                    parent.right = succssor;
                };

                // 3. 将删除节点的左子树 = current.left
                succssor.left = current.left;

            }
            ;


        };

        // 找后继的方法
        BinarySearchTree.prototype.getSuccssor = function (delNode) {
            // 1. 定义变量,保存找到的后继
            var succssor = delNode;
            var current = delNode.right;
            var succssorParent = delNode;

            // 2. 循环查找
            while (current != null){
                succssorParent = succssor;
                succssor = current;
                current = current.left;
            }

            // 3. 判断寻找到的后继节点是否就是delNode的right节点
            if (succssor != delNode.right){
                succssorParent.left = succssor.right;

                succssor.right = delNode.right;
            }
            return succssor;
        }
4.3.3 测试代码
bst.remove(9);
    bst.remove(7);
    bst.remove(15);
    var resultString = "";
    bst.preOrderTraversal(function (key) {
        resultString += key + " "
    });
    alert(resultString);

5. 树的平衡性

5.1 定义

Nodejs 物联网_Nodejs 物联网_33

5.2 常见的平衡树
5.2.1 AVL树

Nodejs 物联网_二叉树_34

5.2.2 红黑树

Nodejs 物联网_Nodejs 物联网_35

6. 红黑树

6.1 规则

Nodejs 物联网_二叉树_36


Nodejs 物联网_二叉树_37

6.2 相对平衡

Nodejs 物联网_数据结构_38


Nodejs 物联网_树结构_39

6.3 变换
6.3.1 变色

Nodejs 物联网_数据结构_40

6.3.2 旋转

(1)左旋转

Nodejs 物联网_算法_41


(2)右旋转

Nodejs 物联网_二叉树_42

6.3.3 插入操作
6.3.3.1 插入分析

Nodejs 物联网_树结构_43


插入的节点默认是红色

Nodejs 物联网_二叉树_44


Nodejs 物联网_Nodejs 物联网_45


(1)情况一:

Nodejs 物联网_算法_46


(2)情况二:

Nodejs 物联网_树结构_47


(3)情况三:

Nodejs 物联网_Nodejs 物联网_48


(4)情况四:

Nodejs 物联网_树结构_49


(5)情况五 :

Nodejs 物联网_数据结构_50

6.3.3.2 红黑树插入案例

插入:10 9 8 7 6 5 4 3 2 1

Nodejs 物联网_数据结构_51


Nodejs 物联网_树结构_52


Nodejs 物联网_树结构_53


Nodejs 物联网_树结构_54


Nodejs 物联网_Nodejs 物联网_55


Nodejs 物联网_Nodejs 物联网_56


Nodejs 物联网_数据结构_57


Nodejs 物联网_Nodejs 物联网_58