算法-二叉查找树

1.什么是二叉查找树

树在计算机中,是一种非线性的数据结构,以分层的方式存储数据。所以,树被经常用来存储具有层级关系的数据,比如文件系统中的文件。本文主要是来分析一种特殊的树,二叉树。二叉树每个节点不允许超过两个,通过这个特性,我们可以写出高效的插入、删除、查找程序。二叉树中,有一种树更为特殊,叫做二叉查找树。在二叉查找树中,相对较小的值保存在做节点中,较大的值保存在又节点中。这种特性,使得我们可以实现查找效率很高的数据结构。

2.实现二叉查找树

要实现一个二叉查找树,首先我们要定义一个Node。二叉树是由不同的Node组成,但是每个Node的基本特性是不变的。每个节点,应该有一个父节点,一个左节点的指向链接,一个右节点的指向链接(指向链接表达得不是很对,欢迎指正)。

function Node(data, left, right) {
    this.data = data;
    this.left = left;
    this.right = right;
}

二叉查找树(Binary Search Tree),可别不知道它的英文怎么写。
首先我们创建一个类BST,一个最基本的二叉查找树。什么是最基本的二叉查找树呢?只有一个父节点,这就是最基本的二叉查找树。

function BST() {
    this.root = null
}

3.插入节点

接下来我们需要实现插入操作,使得这个二叉查找树丰满起来

BST.prototype = {
    insert: function(data) {
        let node = new Node(data, null, null);
        if (this.root == null) {
            this.root = node;
        } else {
            let currentNode = this.root;
            let parentNode;
            while(true) {
                parentNode = currentNode;
                if (data < parentNode.data) {
                    currentNode = currentNode.left;
                    if (currentNode == null) {
                        parentNode.left = node;
                        break;
                    }
                } else {
                    currentNode = currentNode.right;
                    if (currentNode.right == null) {
                        parentNode.right = node;
                        break;
                    }
                }
            }
        }
    }
}

这样,我们可以给二叉查找树添加数据了

let bst = new BST()
bst.insert(20);
bst.insert(11);
bst.insert(5);
bst.insert(30);
bst.insert(10);
bst.insert(8);
bst.insert(3);
bst.insert(22);

有了数据,就不得不说二叉查找树的遍历方式。二叉查找树的遍历方式有3中,即前序遍历,中序遍历,后续遍历。那么如何来理解这三种遍历方式呢?其实用代码的方式是最容易理解的。

前序遍历

traversal: function(node) {
    if(node != null) {
        console.log(node.data);
        this.traversal(node.left);
        this.traversal(node.right);
    }
} 

中序遍历

traversal: function(node) {
    if(node != null) {
        this.traversal(node.left);
        console.log(node.data);
        this.traversal(node.right);
    }
}

后序遍历

traversal: function(node) {
    if(node != null) {
        this.traversal(node.left);
        this.traversal(node.right);
        console.log(this.data);
    }
}   

(以上代码,属于BST.prototype中)
从代码上看,前中后,三种遍历方式,仅仅只是打印本节点顺序的不同而已。记住这三种顺序,就能很轻易的理解什么是前序遍历、后序遍历和中序遍历。
日常生活中,中序遍历最常见,中序遍历后,打印的顺序是从左到右,即从小到大排列。

4.查找给定值

在二叉查找树上查找定值,需要比较每个节点上的数值和给定值的大小。通过大小比较,来确定是向左遍历还是向右遍历

find: function(node, data) {
    if(data == node.data) {
        return 'this value in the bst';
    } else if (data < node.data) {
        return this.find(node.left, data);
    } else {
        return this.find(node.right, data);
    }
    return 'this value not in the bst';
}

5.从二叉树删除节点

删除节点是整个二叉树最复杂的操作,所需要分析的情况也很多。
a.删除节点没有左子树和右子树

这种情况最好解决,直接删除掉这个节点

b.删除节点只有右子树

删除这个节点,并将这个节点的右子树的父节点指向删除节点的父节点

c.删除节点只有左子树

删除这个节点,并将这个节点的左子树的父节点指向删除节点的父节点

d.删除节点既有左子树,又有右子树,操作就有点麻烦了

简单的来说,可以用3步来操作。
1)找到这个节点
2)从这个节点的左子树中找到最大的一个节点,或者从这个节点的右子树中找到最小节点,将找到的最大(小)节点的值复制给当前节点
3)删除这个最大(小)节点,此时,删除这个最大(小)节点的情况,就属于a种情况了

代码如下

removeNode: function(node, data) {
    if (node == null) {
        return null;
    }
    if(data == node.data) {
        if(node.left == null && node.right == null){
            return null;
        } else if (node.left == null) {
            return node.right;
        } else if (node.right == null){
            return node.left;   
        }
        let maxNodeOfLeftLeaves = getMaxNodeOfLeftLeaves(node.left);
        node.data = maxNodeOfLeftLeaves.data;
        node.left = this.removeNode(node.left, node.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;
    }
},
getMaxNodeOfLeftLeaves: function(node) {
    //找到左边子树最大的节点
    let current = node;
    while (current.right) {
        current = current.right;
    }
    return current;
}

二叉查找树其实是一个很基础的数据结构,只要理解什么是二叉查找树,就能很好的写出相关的代码实现。

有兴趣可以关注我的公众号

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值