Golang 中二叉搜索树的实现及常用操作,数据结构系列原文:flaviocopes.com,翻译已获作者授权。
概念
树(tree):一种分层的数据结构,类比家谱
二叉树(binary tree):每个节点最多只有 2 个子节点的树
二叉搜索树(binary search tree):左节点的值均小于右节点值的二叉树
二叉搜索树
常用操作与节点定义
1 2 3 4 5 6 7 8 9
| Insert(v) Search(k) Remove(v) Min() Max() InOrderTraverse() PreOrderTraverse() PostOrderTraverse() String()
|
同样使用 genny 提供代码的复用性,树类型命名为: ItemBinarySearchTree
,树节点的结构体定义如下:
1 2 3 4 5 6
| type Node struct { key int value Item left *Node right *Node }
|
key 是各节点的位置在先序遍历中的序号,key 的值这里使用 int,可以是任意可比较的数据类型。
插入操作与遍历
插入操作需要使用到递归,插入操作需要从上到下查找新节点在树中合适的位置:新节点的值小于任意节点,则向左子树继续寻找,同理向右子树查找,直到查找到树叶节点再插入。
遍历操作有三种方式:
中序遍历(in-order):左子树–>根结点–> 右树:1->2->3->4->5->6->7->8->9->10->11
先序遍历(pre-order):根结点–>左子树–>右子树:8->4->2->1->3->6->5->7 >10->9->11
后序遍历(post-order):左子树–>右子树–>根结点:1->3->2->5->7->6->4->9->11->10->8
String()
可视化树结构:
代码实现
Insert
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| func (tree *ItemBinarySearchTree) Insert(key int, value Item) { tree.lock.Lock() defer tree.lock.Unlock() newNode := &Node{key, value, nil, nil} if tree.root == nil { tree.root = newNode } else { insertNode(tree.root, newNode) } }
func insertNode(node, newNode *Node) { if newNode.key < node.key { if node.left == nil { node.left = newNode } else { insertNode(node.left, newNode) } } else { if node.right == nil { node.right = newNode } else { insertNode(node.right, newNode) } } }
|
Search
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| func (tree *ItemBinarySearchTree) Search(key int) bool { tree.lock.RLock() defer tree.lock.RUnlock() return search(tree.root, key) } func search(node *Node, key int) bool { if node == nil { return false } if key < node.key { return search(node.left, key) } if key > node.key { return search(node.right, key) } return true }
|
Remove
删除节点的流程
先递归查找,再删除节点。但在删除时需根据节点拥有子节点的数量,分如下 3 种情况:
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| func (tree *ItemBinarySearchTree) Remove(key int) { tree.lock.Lock() defer tree.lock.Unlock() remove(tree.root, key) }
func remove(node *Node, key int) *Node { if node == nil { return nil }
if key < node.key { node.left = remove(node.left, key) return node } if key > node.key { node.right = remove(node.right, key) return node }
if node.left == nil && node.right == nil { node = nil return node }
if node.left == nil { node = node.right return node } if node.right == nil { node = node.left return node }
mostLeftNode := node.right for { if mostLeftNode != nil && mostLeftNode.left != nil { mostLeftNode = mostLeftNode.left } else { break } } node.key, node.value = mostLeftNode.key, mostLeftNode.value node.right = remove(node.right, node.key) return node }
|
Min、Max
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| func (tree *ItemBinarySearchTree) Min() *Item { tree.lock.RLock() defer tree.lock.RUnlock() node := tree.root if node == nil { return nil } for { if node.left == nil { return &node.value } node = node.left } }
func (tree *ItemBinarySearchTree) Max() *Item { tree.lock.RLock() defer tree.lock.RUnlock() node := tree.root if node == nil { return nil } for { if node.right == nil { return &node.value } node = node.right } }
|
Traverse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| func (tree *ItemBinarySearchTree) PreOrderTraverse(printFunc func(Item)) { tree.lock.RLock() defer tree.lock.RUnlock() preOrderTraverse(tree.root, printFunc) } func preOrderTraverse(node *Node, printFunc func(Item)) { if node != nil { printFunc(node.value) preOrderTraverse(node.left, printFunc) preOrderTraverse(node.right, printFunc) } }
func (tree *ItemBinarySearchTree) PostOrderTraverse(printFunc func(Item)) { tree.lock.RLock() defer tree.lock.RUnlock() postOrderTraverse(tree.root, printFunc) } func postOrderTraverse(node *Node, printFunc func(Item)) { if node != nil { postOrderTraverse(node.left, printFunc) postOrderTraverse(node.right, printFunc) printFunc(node.value) } }
func (tree *ItemBinarySearchTree) InOrderTraverse(printFunc func(Item)) { tree.lock.RLock() defer tree.lock.RUnlock() inOrderTraverse(tree.root, printFunc) } func inOrderTraverse(node *Node, printFunc func(Item)) { if node != nil { inOrderTraverse(node.left, printFunc) printFunc(node.value) inOrderTraverse(node.right, printFunc) } }
|
String
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| func (tree *ItemBinarySearchTree) String() { tree.lock.Lock() defer tree.lock.Unlock() if tree.root == nil { println("Tree is empty") return } stringify(tree.root, 0) println("----------------------------") } func stringify(node *Node, level int) { if node == nil { return }
format := "" for i := 0; i < level; i++ { format += "\t" } format += "----[ " level++ stringify(node.left, level) fmt.Printf(format+"%d\n", node.key) stringify(node.right, level) }
|
测试用例:tree_test.go
总结
对于二叉搜索树的操作,增删查都与递归相关,所以在实现时一定要分析清楚递归的终止条件,在正确的条件下 return
,避开死循环