数据结构的那些事(三)

继续前面系列,这一章主要分析二叉树和二叉查找树。 树的定义: node

咱们先来了解一下有关树的术语:一棵树最上面的节点称为根节点,若是下面链接多个节点,那么该节点称为父节点,它下面的节点称为子节点。没有任何子节点的节点称为叶子节点。

二叉树是一种特殊的树。它的子节点个数不超过两个,一个父节点左边的称为左节点,右边的称为右节点。

正是由于二叉树的这些特性,让它的查找效率极高,从而引出了二叉查找树,左子节点保存相对父节点较小的值,右节点保存相对父节点较大的值算法

接下来,咱们来一块儿实现这个二叉查找树。 咱们要定义一个对象Node来表示这棵树:数组

function Node(data, left, right) {
    this.data = data;
    this.left = left;
    this.right = right;
    this.show = show;
}
function show() {
    return this.data;
}
复制代码

这个Node对象能够保存数据,也保存和其余节点的连接。 如今,能够建立一个类,用来表示二叉查找树,简称BSTbash

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

这个BST的insert方法有点复杂,咱们来分析下都作了那些事:数据结构

(1)首先建立一个没有左右子节点的Node实例;post

(2)若是这是第一个插入BST的节点就做为根节点;ui

(3)不是的话,获取这个根节点赋值给current变量往下走this

(4)进入一个循环判断,找到正确的插入点会跳出循环spa

(5)若是待插入节点保存的数据小于当前节点,则设新的当前为原节点的左节点code

(6)若是当前节点的左节点为null,就将新的节点插入这个位置,退出循环,反之,继续往下循环查找。

(7)查找右节点也相似。

有了insert方法咱们就能插入节点建立一个二叉查找树的数据结构。

有了二叉查找树,接下来咱们该考虑怎么遍历这个BST

有三种遍历BST的方法:分别是中序,先序和后序,其中中序遍历最为常见。

咱们先来看下中序遍历是如何工做的:

有了这个思路咱们就能够写出这个中序遍历的方法:

function inOrder(node) {
    if (!(node == null)) {
        inOrder(node.left);
        putstr(node.show() + " ");
        inOrder(node.right);
    }
}
复制代码

这里用到了递归的思想,该方法须要以升序访问树中全部节点,因此它首先会递归查找到最下面的左叶子节点,而后访问左子树,再访问根节点,最后访问右子树。

先序遍历:

上面是先序遍历的访问路径, 根据上面的思路能够写出这样的代码:

preOrder(node) {
    if (!(node == null)) {
        putstr(node.show() + " ");
        preOrder(node.left);
        preOrder(node.right);
    }
}

复制代码

注意:中序遍历和先序遍历的惟一区别,就是if语句中的代码顺序。

最后是后序遍历

function postOrder(node){
    if(!(node == null)){
        postOrder(node.left);
        postOrder(node.right);
        putstr(node.show() + " ")
    }
}
复制代码

查找最小值,最大值和给定值

由于较小的值老是在左子节点,在BST上查找最小值,只要遍历左子树,直到找到最后一个节点:

function getMin(){
    let current = this.root;
    while(!(current.left == null)){
        current = current.left;
    }
    return current.data
}
复制代码

查找最大值只要遍历右子树直到找到最后一个节点便可。

查找给定值,稍微麻烦点,咱们须要将给定值和当前节点进行比较,来决定是左遍历仍是右遍历。

function find(data){
    let current = this.root;
    while(current !=null){
        if(current.data == data){
            return current
        }
        else if(data<current.data){
            current = current.left
        }else{
            current = current.right
        }
    }
    return null;
}
复制代码

若是找到给定值,返回给定值,找不到会返回null

从二叉树上删除节点

从BST上删除节点的操做最复杂,由于咱们要考虑几种状况:

(1)是否包含待删除的数据

(2)待删除节点是不是叶子节点

(3)待删除节点是否只包含一个子节点

(4)待删除节点是否包含两个子节点

为了方便,咱们将删除节点拆分红两个方法,remove()方法接收待删除的数据,removeNode()方法删除节点:

function remove(data){
    root = removeNode(this.root, data)
}

function 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 = getSmallest(node.right);
        node.data = tempNode.data;
        node.right = removeNode(node.right, tempNode.data);
        return node
    }
    else if (data < node.data){
        node.left = removeNode(node.left, data);
    }
    else {
        node.right = removeNode(node.right, data);
    }
}
复制代码

上面,咱们就完成了二叉查找树的增删改查。

咱们以前有说到:

数组 的搜索比较方便,能够直接用下标,但删除或者插入某些元素就比较麻烦。 链表 与之相反,删除和插入元素很快,但查找很慢。 二叉查找树 就既有链表的好处,也有数组的好处。 在处理大批量的动态的数据是比较有用。

可是,人无完人,当考虑到二叉查找树的随机性,在最坏的状况下(就是那种一条腿的感受),时间复杂度和顺序查找差很少,这就让人有点没法接受了。

究其缘由,仍是由于左右子树相差悬殊致使的,因而有人经过算法研究出了平衡二叉查找树 ,也就是红黑树

就长这样,这个要讲清楚又是一个大东西,博主也没彻底研究明白,等后续有时间弄清楚了奉贤给你们0.0

相关文章
相关标签/搜索