【问题标题】:What's an idiomatic way to implement a red-black tree in Go?在 Go 中实现红黑树的惯用方法是什么?
【发布时间】:2014-06-23 19:24:24
【问题描述】:

我是 Go 新手,已经实现了二叉搜索树。树可以存储任何值(具体来说,任何实现 interface{} 的值)。

我想在这个实现的基础上创建一个自平衡的红黑树。在面向对象的语言中,我会定义一个 BinarySearchTree 的子类,添加一个 color 数据成员,然后重写 Insert 方法以执行平衡操作。

问题:如何在不重复代码的情况下在 Go 中实现二叉搜索树和红黑树?

当前的二叉搜索树实现

这是我的二叉搜索树实现:

package trees

import (
    "github.com/modocache/cargo/comparators"
    "reflect"
)

type BinarySearchTree struct {
    Parent *BinarySearchTree
    Left   *BinarySearchTree
    Right  *BinarySearchTree
    Value  interface{}      // Can hold any value
    less   comparators.Less // A comparator function to determine whether
                            // an inserted value is placed left or right
}

func NewBinarySearchTree(value interface{}, less comparators.Less) *BinarySearchTree {
    return &BinarySearchTree{Value: value, less: less}
}

func (tree *BinarySearchTree) Insert(value interface{}) *BinarySearchTree {
    if tree.less(value, tree.Value) {
        return tree.insertLeft(value)
    } else {
        return tree.insertRight(value)
    }
}

func (tree *BinarySearchTree) insertLeft(value interface{}) *BinarySearchTree {
    if tree.Left == nil {
        tree.Left = &BinarySearchTree{Value: value, Parent: tree, less: tree.less}
        return tree.Left
    } else {
        return tree.Left.Insert(value)
    }
}

func (tree *BinarySearchTree) insertRight(value interface{}) *BinarySearchTree {
    if tree.Right == nil {
        tree.Right = &BinarySearchTree{Value: value, Parent: tree, less: tree.less}
        return tree.Right
    } else {
        return tree.Right.Insert(value)
    }
}

func (tree *BinarySearchTree) Find(value interface{}) *BinarySearchTree {
    if reflect.DeepEqual(value, tree.Value) {
        return tree
    } else if tree.less(value, tree.Value) {
        return tree.findLeft(value)
    } else {
        return tree.findRight(value)
    }
}

func (tree *BinarySearchTree) findLeft(value interface{}) *BinarySearchTree {
    if tree.Left == nil {
        return nil
    } else {
        return tree.Left.Find(value)
    }
}

func (tree *BinarySearchTree) findRight(value interface{}) *BinarySearchTree {
    if tree.Right == nil {
        return nil
    } else {
        return tree.Right.Find(value)
    }
}

以下是如何使用此结构的示例:

tree := NewBinarySearchTree(100, func(value, treeValue interface{}) bool {
    return value.(int) < treeValue.(int)
})
tree.Insert(200)
tree.Insert(300)
tree.Insert(250)
tree.Insert(150)
tree.Insert(275)
tree.Find(250) // Returns tree.Right.Right.Left

希望(但不可能)实现红黑树

我想像这样“扩展”BinarySearchTree struct

type RedBlackTree struct {
    Parent *RedBlackTree     // These must be able to store
    Left   *RedBlackTree     // pointers to red-black trees
    Right  *RedBlackTree
    Value  interface{}
    less   comparators.Less
    color RedBlackTreeColor  // Each tree must maintain a color property
}

然后像这样“覆盖”.Insert() 方法:

func (tree *RedBlackTree) Insert(value interface{}) *RedBlackTree {
    var inserted *RedBlackTree

    // Insertion logic is identical to BinarySearchTree
    if tree.less(value, tree.Value) {
        inserted = tree.insertLeft(value)
    } else {
        inserted tree.insertRight(value)
    }

    // .balance() is a private method on RedBlackTree that balances
    // the tree based on each node's color
    inserted.balance()

    // Returns a *RedBlackTree
    return inserted
}

不过,我不认为这是惯用的 Go 代码。

  • 由于BinarySearchTree 是用指向其他BinarySearchTree 结构的指针定义的,“扩展”BinarySearchTreeRedBlackTree 仍然具有指向BinarySearchTree 对象的指针。
  • 无法“覆盖”.Insert()。我唯一的选择是定义另一种方法,例如.BalancedInsert()

目前正在尝试

我目前正在尝试的一个想法是定义一个这样的接口:

type BinarySearchable interface {
    Parent() *BinarySearchable
    SetParent(searchable *BinarySearchable)

    Left() *BinarySearchable
    SetLeft(searchable *BinarySearchable)

    Right() *BinarySearchable
    SetRight(searchable *BinarySearchable)

    Value() interface{}
    Less() comparators.Less
    Insert(searchable *BinarySearchable) *BinarySearchable
    Find(value interface{}) *BinarySearchable
}

BinarySearchTreeRedBlackTree 然后将实现这些接口。然而,一个问题是如何共享.Insert() 逻辑。也许定义一个每个结构将使用的私有函数?

欢迎提出任何建议。

【问题讨论】:

    标签: go binary-search-tree dry red-black-tree


    【解决方案1】:

    您可以对example 使用嵌入:

    type A struct{}
    
    func (a *A) fn()  { fmt.Println("A.fn") }
    func (a *A) fn1() { fmt.Println("A.fn1") }
    
    type B struct{ *A }
    
    func (a *B) fn() { fmt.Println("B.fn") }
    
    func main() {
        b := &B{&A{}}
        b.fn()
        b.fn1()
    }
    

    这应该覆盖 BST 的 Insert 但保留所有其他功能:

    type RedBlackTree struct {
        *BinarySearchTree
        color RedBlackTreeColor // This is the only new attribute
    }
    
    func (tree *RedBlackTree) Insert(value interface{}) *RedBlackTree {}
    

    http://golang.org/doc/effective_go.html#embedding

    //编辑

    重新阅读问题,您将不得不稍微改变逻辑

    type RedBlackTree struct {
        *BinarySearchTree
    }
    type rbtValue struct {
        value interface{}
        Color RedBlackTreeColor
    }
    func (tree *RedBlackTree) Insert(value interface{}) (inserted *RedBlackTree) {
        if tree.less(value, tree.Value) {
            inserted = tree.insertLeft(&rbt{value, Red})
        } else {
            inserted = tree.insertRight(&rbt{value, Black})
        }
        inserted.balance()
        return inserted
    }
    

    然后制作一个适用于tree.Value.(rbtValue).Value的比较器

    【讨论】:

    • 我认为这行不通。我知道嵌入 Go,但问题是 .insertLeft() 设置了 tree.Left = NewBinarySearchTree()。我需要它是tree.Left = NewRedBlackTree(),因为红黑树中的每个节点都必须保持一个颜色属性。但是,由于Left 被定义为*BinarySearchTree,我无法将其设置为*RedBlackTree 的实例。我的原始帖子包含一个type RedBlackTree struct 的定义,它有指向*BinarySearchTree 的指针,这是不正确的。我更新了我原来的帖子。
    • 我意识到答案后,检查编辑,但是你的答案是要走的路。
    • 感谢您的回答,但它帮助我找到了正确的方向。顺便说一句,我想知道:您使用指针嵌入,即:*BinarySearchTree。搜索互联网我发现了一个不使用指针嵌入的示例,即:BinarySearchTree。有区别吗?
    • 好吧,我确实使用指针来避免复制,这是使用指针的坏习惯,除非它是本机类型或非常简单的结构。
    【解决方案2】:

    这就是我想出的。我宁愿接受另一个答案,但这是迄今为止最好的。

    BinarySearchable 接口

    BinarySearchTreeRedBlackTree 都符合这个接口。该文件还定义了所有二进制可搜索结构共有的函数,包括insert().find()leftRotate() 等。

    为了动态创建各种类型的对象,insert() 函数采用childConstructor 函数参数。 BinarySearchTreeRedBlackTree 使用此函数创建任意类型的子树。

    // binary_searchable.go
    
    type BinarySearchable interface {
        Parent() BinarySearchable
        SetParent(searchable BinarySearchable)
        Left() BinarySearchable
        SetLeft(searchable BinarySearchable)
        Right() BinarySearchable
        SetRight(searchable BinarySearchable)
        Value() interface{}
        Insert(value interface{}) BinarySearchable
        Find(value interface{}) BinarySearchable
        Less() comparators.Less
    }
    
    type childConstructor func(parent BinarySearchable, value interface{}) BinarySearchable
    
    func insert(searchable BinarySearchable, value interface{}, constructor childConstructor) BinarySearchable {
        if searchable.Less()(value, searchable.Value()) {
            if searchable.Left() == nil {
                // The constructor function is used to
                // create children of dynamic types
                searchable.SetLeft(constructor(searchable, value))
                return searchable.Left()
            } else {
                return searchable.Left().Insert(value)
            }
        } else {
            if searchable.Right() == nil {
                searchable.SetRight(constructor(searchable, value))
                return searchable.Right()
            } else {
                return searchable.Right().Insert(value)
            }
        }
    }
    

    BinarySearchTree

    这是嵌入在其他树结构中的“基础”结构。它提供了BinarySearchable 接口方法的默认实现,以及每棵树用于存储其子级的数据属性。

    // binary_search_tree.go
    
    type BinarySearchTree struct {
        parent BinarySearchable
        left   BinarySearchable
        right  BinarySearchable
        value  interface{}
        less   comparators.Less
    }
    
    func (tree *BinarySearchTree) Parent() BinarySearchable {
        return tree.parent
    }
    
    func (tree *BinarySearchTree) SetParent(parent BinarySearchable) {
        tree.parent = parent
    }
    
    // ...setters and getters for left, right, value, less, etc.
    
    func (tree *BinarySearchTree) Insert(value interface{}) BinarySearchable {
        // Pass `insert()` a constructor that creates a `*BinarySearchTree`
        constructor := func(parent BinarySearchable, value interface{}) BinarySearchable {
            return &BinarySearchTree{value: value, less: tree.less, parent: parent}
        }
        return insert(tree, value, constructor).(*BinarySearchTree)
    }
    
    func (tree *BinarySearchTree) Find(value interface{}) BinarySearchable {
        return find(tree, value)
    }
    

    RedBlackTree

    这嵌入了BinarySearchTree 并将自定义构造函数传递给insert()。为简洁起见,省略了平衡代码;你可以see the whole file here

    // red_black_tree.go
    
    type RedBlackTree struct {
        *BinarySearchTree
        color RedBlackTreeColor
    }
    
    func NewRedBlackTree(value interface{}, less comparators.Less) *RedBlackTree {
        return &RedBlackTree{&BinarySearchTree{value: value, less: less}, Black}
    }
    
    func (tree *RedBlackTree) Insert(value interface{}) BinarySearchable {
        constructor := func(parent BinarySearchable, value interface{}) BinarySearchable {
            return &RedBlackTree{&BinarySearchTree{value: value, less: tree.less, parent: parent}, Red}
        }
    
        inserted := insert(tree, value, constructor).(*RedBlackTree)
        inserted.balance()
        return inserted
    }
    
    func (tree *RedBlackTree) balance() {
        // ...omitted for brevity
    }
    

    如果有人有更惯用的设计,或改进此设计的建议,请发表答案,我会接受。

    【讨论】:

      猜你喜欢
      • 2014-09-23
      • 2012-11-30
      • 1970-01-01
      • 2023-03-14
      • 2018-10-30
      • 1970-01-01
      • 2010-12-14
      • 2012-08-20
      相关资源
      最近更新 更多