js数据结构与算法 第四天(树结构)

树结构

儿子兄弟表示法:
在这里插入图片描述
在这里插入图片描述

二叉树
如果树中每个节点最多只能有两个子节点,这样的树就成为“二叉树”
在这里插入图片描述
在这里插入图片描述
满二叉树(完美二叉树)
除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树。
完全二叉树
树的节点都是从上到下,从左到右来添加的
在这里插入图片描述
用数组只能表示完全二叉树,表示非完全二叉树时会造成空间的浪费
二叉搜索树BST-Binary Search Tree(二叉排序树/二叉查找树)

若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
先序遍历
先访问根节点,再访问左节点,再访问右节点
在这里插入图片描述
中序遍历
先访问左节点,再访问根节点,最后右节点
在这里插入图片描述
后序遍历
先左节点,再右节点,最后根节点
在这里插入图片描述
二叉搜索树的封装
二叉树的封装主要用到递归方法
以以下为例:
在这里插入图片描述
二叉树的删除方法比较复杂,
先找到要删除的节点,如果没有找到,则不需要删除,再判断要删除的节点的情况:

  • 该节点是叶子节点
    找到要删除的节点的父节点,再将父节点的左子节点或右子节点置为null
  • 该节点有一个子节点
  • 该节点有两个子节点
    找要删除节点的前驱和后继:
    前驱一般是左子树的最大值,后继是右子树的最小值
    封装代码:
    属性:
function BinarySearchTree() {
	//封装节点的属性
	function Node(key) {
	    this.key = key;
	    this.left = null; //左节点
	    this.right = null; //右节点
	}
	//根节点
	this.root = null;

方法:
1.insert()方法

//1.insert()方法
BinarySearchTree.prototype.insert = function(key) {
    //创建节点
    var newnode = new Node(key);
    //判断根节点是否有值
    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);
        }
    }
}

2.search()方法

//2.search()方法
BinarySearchTree.prototype.search = function(key) {
    //获取根节点
    var node = this.root;
    //循环搜索key
    while (node != null) {
        if (key < node.key) {
            node = node.left;
        } else if (key > node.key) {
            node = node.right
        } else {
            return true;
        }
    }
    return false;
}

3.inOrderTraverse()中序遍历

//3.inOrderTraverse() 中序遍历
BinarySearchTree.prototype.inOrderTraverse = function(handler) {
    this.inOrderTraverseNode(this.root, handler);
}

//中序遍历递归方法
BinarySearchTree.prototype.inOrderTraverseNode = function(node, handler) {
    if (node != null) {
        //处理左子树的节点
        this.inOrderTraverseNode(node.left, handler);
        //处理节点
        handler(node.key);
        //处理右子树的节点
        this.inOrderTraverseNode(node.right, handler);

    }
}

4.preOrderTraverse()先序遍历

//4.preOrderTraverse() 先序遍历
BinarySearchTree.prototype.preOrderTraverse = function(handler) {
    this.preOrderTraverseNode(this.root, handler);
}

//先序遍历递归方法
BinarySearchTree.prototype.preOrderTraverseNode = function(node, handler) {
    if (node != null) {
        //处理经过的节点
        handler(node.key);
        //处理经过的节点的左子节点
        this.preOrderTraverseNode(node.left, handler);
        //处理经过的节点的右子节点
        this.preOrderTraverseNode(node.right, handler);

    }
}

5.postOrderTraverse()后序遍历

//5.postOrderTraverse() 后序遍历
BinarySearchTree.prototype.postOrderTraverse = function(handler) {
    this.postOrderTraverseNode(this.root, handler);
}

//后序遍历递归方法
BinarySearchTree.prototype.postOrderTraverseNode = function(node, handler) {
    if (node != null) {
        //处理经过的节点的左子节点
        this.postOrderTraverseNode(node.left, handler);
        //处理经过的节点的右子节点
        this.postOrderTraverseNode(node.right, handler);
        //处理经过的节点
        handler(node.key);
    }
}

6.min()返回最小值

//6.min() 返回最小值
//最小值就是树最左边的值
BinarySearchTree.prototype.min = function() {
    var node = this.root;
    var key = null;
    while (node != null) {
        key = node.key;
        node = node.left;
    }
    return key;
}

7.max()返回最大值

//7.max() 返回最大值 
//最大值就是树最右边的值
BinarySearchTree.prototype.max = function() {
    //获取根节点
    var node = this.root;
    //不断的向右查找 直到节点为null
    var key = null;
    while (node != null) {
        key = node.key;
        node = node.right;
    }
    return key;
}

8.remove()方法

BinarySearchTree.prototype.remove = function(delkey) {
        //寻找要删除的节点
        var current = this.root;
        var parent = null;
        //要删除节点是父节点的左节点
        var isLeftChild = true;
        while (current.key != delkey) {
            parent = current;
            if (delkey < current.key) {
                isLeftChild = true;
                current = current.left;
            } else {
                isLeftChild = false;
                current = current.right;
            }
            //已经找到最后一个节点还没有找到要删除的那个节点
            if (current = null) {
                return false;
            }
        }
        //找到了要删除的节点  current.key=key
        //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.要删除的节点有一个子节点
        //这里是要删除的节点没有右节点
        else if (current.right == null) {
            //要删除的节点是根节点 但是有一个左子节点
            if (current == this.root) {
                this.root = current.left
            } else if (isLeftChild) {
                parent.left = current.left;
            } else {
                parent.right = current.left;
            }
        }
        //这里是要删除的节点没有左节点
        else if (current.left == null) {
            //要删除的节点是根节点 但是有一个右子节点
            if (current == this.root) {
                this.root = current.right;
            } else if (isLeftChild) {
                parent.left = current.right;
            } else {
                parent.right = current.right;
            }
        }
        //3.删除的节点有两个子节点
        else {
            //获取后继节点
            var successor = this.getSuccssor(current);
            //判断是否是根节点
            if (current == this.root) {
                this.root = successor;
            } else if (isLeftChild) {
                parent.left = successor;
            } else {
                parent.right = successor;
            }
            successor.left = current.left;
        }
    }

    //获取后继结点
    BinarySearchTree.prototype.getSuccssor = function(delnode) {
        //定义变量 找到节点后继
        var successor = delnode;
        var current = delnode.right;
        var successorParent = delnode;
        //循环查找
        while (current != null) {
            successorParent = successor;
            successor = current;
            current = current.left;
        }
        //判断寻找的后继结点是否delnode的右节点
        if (successor != delnode.right) {
            successorParent.left = successor.right;
            successor.right = delnode.right;
        }
        return successor;
    }
}

二叉搜索树的缺陷
比较好的二叉搜索树的数据应该是左右分布均匀的,如果二叉搜索树插入的是有序的数据,则有可能分布不均匀,这种树为非平衡树。
对于平衡二叉树来说,插入/查找等操作的效率是O(logN)
对于一颗非平衡二叉树,相当于一颗链表,查找效率变成了O(N)
**平衡二叉树:**它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值