前言
本文主要讲解的是二叉搜索树,首先会罗列一些树的基本知识,然后再介绍二叉搜索树,讲解动态数据结构的一些基本操作(Insert,query, delete),最后讲解一些随机构成一颗二叉搜索树的期望深度。并用一个编程题来实战一下。
树的基础知识
树有几个关键名词,root,node,leaves,depth,child,parent这些。其实定义一个树的基本的数据结构,只需要这样
type TreeNode Struct{
Val int
Left, Right *TreeNode
}
利用这个node,一颗树的结构就起来了。当然我们还有多叉树(可以用在建立目录)下面这样的结构可以很好的构建一个目录;还有一些2-3-4树,Red-Black树,平衡二叉树等。
type Directory struct {
name string
parent *Directory
children []*Directory
}
下面介绍一下树的遍历
前序遍历
preTraversal(x *node){
if x == nil return
fmt.println(x.val)
preTraversal(x.left)
preTraversal(x.right)
}
遍历得到的序列为:15,6,3,2,4,7,13,9,18,17,20
中序遍历
midTraversal(x *node){
if x == nil return
midTraversal(x.left)
fmt.println(x.val)
midTraversal(x.right)
}
遍历得到的序列为:2,3,4,6,7,9,13,15,17,18,20
后序遍历
postTraversal(x *node){
if x == nil return
postTraversal(x.left)
postTraversal(x.right)
fmt.println(x.val)
}
遍历得到的序列为2,4,3,9,13,7,6,17,20,18,15
二叉树(BST)
定义呢就是,left child < its parent < right child,像这样就构成的树,就是二叉搜索树。
查询
查询的方法很简单,就是把前序遍历改一手。
func Query(x *Node, k int) *Node{
if x == nil || x.Val == k {return}
if x.Val < k{
return Query(x.Right, k)
}else{
return Query(x.Left, k)
}
}
插入和删除
插入和删除就涉及到了动态数据结构的维护。因为插入和删除树的时候,可能涉及到树的结构的改变。这也是设计自己需要的数据结构所需要严格遵守的。
插入
实现代码是这样的:
func TreeInsert(T *Tree, z *Node){
y = NIL
x = T.root
while x != nil{
y = x
if z.Val < x.Val{
x = x.left
}
else{
x = x.right
}
}
z.P = y
if y == nil {T.root = z}
if z.Val < y.Val {y.left = z}
else {y.right = z}
}
删除
讲道理这个删除操作还是蛮精妙的。首先对删除操作进行了分析,得出了4种具体的情况。a,b表示得很简单,就是直接将子树接上。对于c和d我们其实可以使用一种情况来处理,首先是要找到一个替换z的结点。这个节点实则就是z右子树的最小节点。然后再实现相应的操作。
func Translate(T, u, v){
if u.p == nil{T.root = v}
else if u == u.p.left {u.p.left = v}
else {u.p.right = v}
if v != nil {v.p = u.p}
}
func TreeDelete(T, z){
if z.left == nil {Translate(T, z, z.right)}
else if z.rght == nil {Translate(T, z, z.left)}
else {
y = Tree.Minimum(z.right)
if y.p != z{
Translate(T, y, y.right)
y.right = z.right
y.right.p = y
}
Translate(T, z. y)
y.left = z.left
y.left.p = y
}
}
随机构建BST
为什么随机构建二叉树,是为了保证BST的树的深度的期望值为 l g n lgn lgn。证明就省略。