红黑树在Go语言中的实现有哪些惯用的方式?

huangapple go评论75阅读模式
英文:

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结构体的指针,一个“扩展”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()逻辑。也许可以定义一个每个结构体都会使用的私有函数?

欢迎提出任何建议。

英文:

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 (
&quot;github.com/modocache/cargo/comparators&quot;
&quot;reflect&quot;
)
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 &amp;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 = &amp;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 = &amp;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) &lt; 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&#39;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 other BinarySearchTree structs, a RedBlackTree that "extends" BinarySearchTree still has pointers to BinarySearchTree 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 接口

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() 传递了一个自定义的构造函数。为了简洁起见,平衡代码被省略了;你可以在这里查看整个文件

// 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 &amp;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 &amp;RedBlackTree{&amp;BinarySearchTree{value: value, less: less}, Black}
}
func (tree *RedBlackTree) Insert(value interface{}) BinarySearchable {
constructor := func(parent BinarySearchable, value interface{}) BinarySearchable {
return &amp;RedBlackTree{&amp;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(&quot;A.fn&quot;) }
func (a *A) fn1() { fmt.Println(&quot;A.fn1&quot;) }
type B struct{ *A }
func (a *B) fn() { fmt.Println(&quot;B.fn&quot;) }
func main() {
b := &amp;B{&amp;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(&amp;rbt{value, Red})
} else {
inserted = tree.insertRight(&amp;rbt{value, Black})
}
inserted.balance()
return inserted
}

Then make a comparator that works on tree.Value.(rbtValue).Value

huangapple
  • 本文由 发表于 2014年5月7日 09:36:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/23507266.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定