美文网首页
Go:实现二叉搜索树(BST)

Go:实现二叉搜索树(BST)

作者: Go语言由浅入深 | 来源:发表于2021-12-07 23:57 被阅读0次

    二叉搜索树是一种支持快速查找、增加和删除元素的数据结构。二叉搜索树中的节点是有序的,因此支持快速查找。该数据结构由P.F.Windley、A.D.Booth、A.J.T.Colin和T.N.Hibbard发明的。

    二叉搜索树的平均空间复杂度是O(n),插入、删除和搜索操作的时间就复杂的是O(logn)。二叉搜索树节点包含以下属性:

    • 一个整型key值
    • 一个整型value值
    • 包含左节点leftNode和右节点rightNode
    • 左节点key小于当前节点,右节点key大于当前节点

    节点定义代码如下:

    type TreeNode struct {
        key int
        value int
        leftNode *TreeNode
        rightNode *TreeNode
    }
    

    二叉搜索树结构体定义,其包含一个根节点rootNode是TreeNode类型,以及一个互斥锁sync.RWMutex类型。二叉搜索树的遍历是从根节点出发的,然后是左节点和右节点:

    type BinarySearchTree struct {
        rootNode *TreeNode
        lock sync.RWMutex
    }
    

    定义好了二叉搜索树结构体类型,接下来实现其方法。

    插入方法:InsertElement

    该方法作用是将一个包含key和value的节点插入到二叉搜索树中。插入之前要获取锁,插入完成释放锁。

    func (tree *BinarySearchTree)InsertElement(key int, value int)  {
        tree.lock.Lock()
        defer tree.lock.Unlock()
        var treeNode *TreeNode
        treeNode = &TreeNode{
            key:       key,
            value:     value,
            leftNode:  nil,
            rightNode: nil,
        }
        if tree.rootNode == nil {
            tree.rootNode = treeNode
        } else {
            insertTreeNode(tree.rootNode, treeNode)
        }
    }
    

    先根据key,value创建treeNode节点。然后判断根节点是否为空,如果是空就将新增的节点赋值给根节点。如果根节点不为空,调用insertTreeNode函数找到合适的位置插入。

    func insertTreeNode(rootNode *TreeNode, newTreeNode *TreeNode)  {
        if newTreeNode.key < rootNode.key {
            if rootNode.leftNode ==  nil {
                rootNode.leftNode = newTreeNode
            } else {
                insertTreeNode(rootNode.leftNode, newTreeNode)
            }
        } else {
            if rootNode.rightNode == nil{
                rootNode.rightNode = newTreeNode
            } else{
                insertTreeNode(rootNode.rightNode, newTreeNode)
            }
        }
    }
    

    根据二叉搜索树的特点,每个节点的key比左节点key要大,比右节点的key要小。因此先和左节点的key比较,如果左节点为空,就将新增节点赋值给左节点,否则继续递归找。同理如果新增节点key比左节点大,就和右节点比较,为空就赋值,否则递归。

    inOrderTraverse方法:顺序遍历二叉搜索树

    该方法按顺序遍历树中的每个节点。先调用RLock()读锁,遍历完后释放:

    func (tree *BinarySearchTree)InOrderTraverseTree(function func(int))  {
        tree.lock.RLock()
        defer tree.lock.RUnlock()
        inOrderTraverseTree(tree.rootNode, function)
    }
    
    func inOrderTraverseTree(treeNode *TreeNode, function func(int))  {
        if treeNode != nil {
            inOrderTraverseTree(treeNode.leftNode, function)
            function(treeNode.value)
            inOrderTraverseTree(treeNode.rightNode, function)
        }
    }
    

    为了避免数据竞争,这里创建一个内部调用函数来实现有序遍历二叉搜索树。根据二叉搜索树特点,先遍历左子树,然后当前节点最后遍历右子树,也称为中序遍历。

    PreOrderTraverse方法:前序遍历

    先遍历当前节点,然后是左子树最后右子树;

    func (tree *BinarySearchTree)PreOrderTraverseTree(function func(int))  {
        tree.lock.RLock()
        defer tree.lock.RUnlock()
        preOrderTraverseTree(tree.rootNode, function)
    }
    
    func preOrderTraverseTree(treeNode *TreeNode, function func(int))  {
        if treeNode != nil {
            function(treeNode.value)
            preOrderTraverseTree(treeNode.leftNode, function)
            preOrderTraverseTree(treeNode.rightNode, function)
        }
    }
    

    PostOrderTraverse方法:后序遍历

    后序遍历:先左子树,然后右子树最后是当前节点

    func (tree *BinarySearchTree)PostOrderTraverseTree(function func(int))  {
        tree.lock.RLock()
        defer tree.lock.RUnlock()
        postOrderTraverseTree(tree.rootNode, function)
    }
    
    func postOrderTraverseTree(treeNode *TreeNode, function func(int))  {
        if treeNode != nil {
            postOrderTraverseTree(treeNode.leftNode, function)
            postOrderTraverseTree(treeNode.rightNode, function)
            function(treeNode.value)
        }
    }
    

    MinNode方法:最小节点

    该方法:用于查找二叉搜索树中key最小的节点值。

    func (tree *BinarySearchTree)MinNode() *int {
        tree.lock.RLock()
        defer tree.lock.RUnlock()
        var treeNode *TreeNode
        treeNode = tree.rootNode
        if treeNode == nil {
            return (*int)(nil)
        }
        for  {
                    //最小值位于左子树
            if treeNode.leftNode == nil {
                return &treeNode.value
            }
            treeNode = treeNode.leftNode
        }
    }
    

    根据二叉搜索树的特点:左子树值都小于当前节点,所以如果当前节点的左节点为空,说明当前节点的key是最小的

    MaxNode方法:查询最大key节点

    该方法和MinNode方法相反,最大子位于当前节点右子树。如果当前节点的右节点为空,说明当前节点是值最大节点。

    func (tree *BinarySearchTree)MaxNode() *int {
        tree.lock.RLock()
        defer tree.lock.RUnlock()
        var treeNode *TreeNode
        treeNode = tree.rootNode
        if treeNode == nil {
            return (*int)(nil)
        }
        for  {
            if treeNode.rightNode == nil {
                return &treeNode.value
            }
            treeNode = treeNode.rightNode
        }
    }
    

    SearchNode方法:根据key查询对应节点是否存在。

    func (tree *BinarySearchTree)SearchNode(key int) bool {
        tree.lock.RLock()
        defer tree.lock.RUnlock()
        return searchNode(tree.rootNode, key)
    }
    
    func searchNode(treeNode *TreeNode, key int) bool {
        if treeNode == nil {
            return false
        }
        if key < treeNode.key{
            return searchNode(treeNode.leftNode, key)
        }
        if key > treeNode.key{
            return searchNode(treeNode.rightNode, key)
        }
        return true
    }
    

    二叉搜索树:当前节点比左子树所有节点大,比右子树所有节点小。因此在查找一个key是否存在时,如果当前节点key比搜索key更小,就继续在左子树查找;如果要搜索key大于当前节点的key就在右子树查找。否则当前节点就是要查找的节点。

    removeNode方法:删除节点

    func (tree *BinarySearchTree) RemoveNode(key int) {
        tree.lock.Lock()
        defer tree.lock.Unlock()
        removeNode(tree.rootNode, key)
    }
    
    func removeNode(treenode *TreeNode, key int) *TreeNode {
        if treenode == nil {
            return nil
        }
        if key < treenode.key {
            treenode.leftNode = removeNode(treenode.leftNode, key)
            return treenode
        }
        if key > treenode.key {
            treenode.rightNode = removeNode(treenode.rightNode, key)
            return treenode
        }
        //key == treeNode.ley
        if treenode.leftNode == nil && treenode.rightNode == nil {
            treenode = nil
            return nil
        }
        //要删除的节点,左节点为空
        if treenode.leftNode == nil {
            treenode = treenode.rightNode
            return treenode
        }
        //要删除的节点,右节点为空
        if treenode.rightNode == nil {
            treenode = treenode.leftNode
            return treenode
        }
        //要删除的节点,左右节点都不为空
        var leftmostrightNode *TreeNode
        leftmostrightNode = treenode.rightNode
        for {
            if leftmostrightNode != nil && leftmostrightNode.leftNode != nil {
                leftmostrightNode = leftmostrightNode.leftNode
            } else {
                break
            }
        }
           //将右子树最小节点值,替换待删除节点的值
        treenode.key, treenode.value = leftmostrightNode.key, leftmostrightNode.value
          //递归删除右子树最小节点
        treenode.rightNode = removeNode(treenode.rightNode, treenode.key)
        return treenode
    }
    

    二叉搜索树删除节点比较复杂,递归的删除。分4种情况:
    1、待删除节点,左右节点为空,直接删除。
    2、待删除节点,左节点为空,直接将右节点赋值给删除节点覆盖
    3、待删除节点,右节点为空,直接将左节点赋值给删除节点覆盖。
    4、待删除节点,左右节点都不为空。用其右子树的最小结点代替待删除结点。代码中的leftmostrightNode节点为右子树最小节点。

    相关文章

      网友评论

          本文标题:Go:实现二叉搜索树(BST)

          本文链接:https://www.haomeiwen.com/subject/hlfoxrtx.html