英文:
What's an idiomatic way to implement a red-black tree in Go?
问题
我是你的中文翻译助手,以下是翻译好的内容:
我刚开始学习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{} // 可以存储任何值
less comparators.Less // 用于确定插入的值是放在左边还是右边的比较函数
}
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) // 返回 tree.Right.Right.Left
我希望像这样“扩展”BinarySearchTree
结构体:
type RedBlackTree struct {
Parent *RedBlackTree // 这些必须能够存储指向红黑树的指针
Left *RedBlackTree // 这些必须能够存储指向红黑树的指针
Right *RedBlackTree
Value interface{}
less comparators.Less
color RedBlackTreeColor // 每个树必须维护一个颜色属性
}
然后像这样“重写”.Insert()
方法:
func (tree *RedBlackTree) Insert(value interface{}) *RedBlackTree {
var inserted *RedBlackTree
// 插入逻辑与BinarySearchTree相同
if tree.less(value, tree.Value) {
inserted = tree.insertLeft(value)
} else {
inserted tree.insertRight(value)
}
// .balance()是RedBlackTree上的一个私有方法,根据每个节点的颜色平衡树
inserted.balance()
// 返回一个*RedBlackTree
return inserted
}
然而,我认为这不是符合Go代码风格的。
- 由于
BinarySearchTree
定义了指向其他BinarySearchTree
结构体的指针,一个“扩展”BinarySearchTree
的RedBlackTree
仍然有指向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
}
然后BinarySearchTree
和RedBlackTree
将实现这些接口。但是一个问题是如何共享.Insert()
逻辑。也许可以定义一个每个结构体都会使用的私有函数?
欢迎提出任何建议。
英文:
I'm new to Go and have implemented a binary search tree. The tree can store any value (specifically, anything that implements interface{}
).
I'd like to build upon this implementation to create a self-balancing red-black tree. In an object-oriented language, I'd define a subclass of BinarySearchTree
that adds a color
data member, then override the Insert
method to perform a balancing operation.
Question: How can I implement a binary search tree and red-black tree in Go without duplicating code?
Current Binary Search Tree Implementation
Here's my binary search tree implementation:
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)
}
}
Here's an example of how this struct may be used:
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
Desired (But Impossible) Red-Black Tree Implementation
I'd like to "extend" the BinarySearchTree
struct
like so:
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
}
And then "override" the .Insert()
method like so:
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
}
I don't think this is idiomatic Go code, however.
- Since
BinarySearchTree
is defined with pointers to otherBinarySearchTree
structs, aRedBlackTree
that "extends"BinarySearchTree
still has pointers toBinarySearchTree
objects. - There's no way to "override"
.Insert()
. My only option is to define another method, such as.BalancedInsert()
.
Currently Trying
One idea I'm currently trying is to define an interface such as this one:
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
}
The BinarySearchTree
and RedBlackTree
will then implement these interfaces. One problem is how to share the .Insert()
logic, however. Perhaps define a private function that each struct will use?
Any and all suggestions welcome.
答案1
得分: 4
这是我想出来的。我更愿意接受另一个答案,但这是目前最好的答案。
BinarySearchable
接口
BinarySearchTree
和 RedBlackTree
都符合这个接口。该文件还定义了所有二叉搜索结构共有的函数,包括 insert()
、find()
、leftRotate()
等等。
为了动态创建不同类型的对象,insert()
函数接受一个 childConstructor
函数参数。BinarySearchTree
和 RedBlackTree
使用这个函数来创建任意类型的子树。
// 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()
传递了一个自定义的构造函数。为了简洁起见,平衡代码被省略了;你可以在这里查看整个文件。
// 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
}
如果有人有更符合惯用法的设计,或者对这个设计有改进的建议,请发表答案,我会接受它。
英文:
This is what I came up with. I'd rather accept another answer, but this is the best so far.
The BinarySearchable
Interface
Both BinarySearchTree
and RedBlackTree
conform to this interface. The file also defines functions common to all binary-searchable structs, including insert()
, .find()
, leftRotate()
, and so on.
In order to dynamically create objects of various types, the insert()
function takes a childConstructor
function parameter. This function is used by BinarySearchTree
and RedBlackTree
to create child trees of arbitrary types.
// 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
This is the "base" struct that is embedded in other tree structs. It provides default implementations of the BinarySearchable
interface methods, as well as the data attributes each tree will use to store their children.
// 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
This embeds BinarySearchTree
and passes a custom constructor to insert()
. The balancing code is omitted for brevity; you can 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
}
If anyone has a more idiomatic design, or suggestions to improve this design, please post an answer and I will accept it.
答案2
得分: 3
你可以使用嵌入来实现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 // 这是唯一的新属性
}
func (tree *RedBlackTree) Insert(value interface{}) *RedBlackTree {}
http://golang.org/doc/effective_go.html#embedding
//edit
重新阅读问题后,你需要稍微改变逻辑:
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
上工作。
英文:
You could use embedding for 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()
}
This should override the BST's Insert
but keep all the other functions:
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
//edit
Rereading the question you will have to change the logic a little
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
}
Then make a comparator that works on tree.Value.(rbtValue).Value
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论