二叉查找树

定义

二叉查找树具有特殊的结构,其任意一个节点,其左子树要小于其父节点,父节点小于其右子树的值。

特点

支持快速的查找,插入,删除。

性质

  1. 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
  2. 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
  3. 任意节点的左、右子树也分别为二叉查找树;
  4. 没有键值相等的节点。

代码实现

package main

import "fmt"

type Node struct {
	Data interface{}
	LeftNode *Node
	RightNode *Node
}

type BinaryTree struct {
	Root *Node
}

type BST struct {
	*BinaryTree
	CompareFun func(v, nodev interface{}) int
}

var CompareFun = func(v, nodev interface{}) int {
	vInt := v.(int)
	nodevInt := nodev.(int)

	if vInt > nodevInt {
		return 1
	} else if vInt < nodevInt {
		return -1
	} else {
		return 0
	}
}

func NewNode(v interface{}) *Node  {
	return &Node{Data:v}
}

func NewBinaryTree(v interface{}) *BinaryTree {
	return &BinaryTree{Root:NewNode(v)}
}

func NewBst(v interface{}, compareFun func(v, nodev interface{}) int) *BST {
	if compareFun == nil {
		return nil
	}
	return &BST{
		BinaryTree: NewBinaryTree(v),
		CompareFun: compareFun,
	}
}

func (bst *BST) InOrder()  {
	p := bst.Root
	if p == nil {
		return
	}

	p.InOrderCore()
}

func (p *Node) InOrderCore() {
	if p == nil {
		return
	}

	if p.LeftNode != nil {
		p.LeftNode.InOrderCore()
	}

	fmt.Println(p.Data)

	if p.RightNode != nil {
		p.RightNode.InOrderCore()
	}
}

func (this *BST) Insert(v interface{}) bool {

	p := this.Root

	if p == nil {
		return false
	}

	for p != nil {
		compareRes := this.CompareFun(v, p.Data)
		if compareRes == 0 {
			return false
		} else if compareRes > 0 {
			if p.RightNode == nil {
				p.RightNode = NewNode(v)
				return true
			}
			p = p.RightNode
		} else if compareRes < 0 {
			if p.LeftNode == nil {
				p.LeftNode = NewNode(v)
				return true
			}
			p = p.LeftNode
		}
	}

	return false
}

func (this *BST) Delete(v interface{}) bool {
	var pp *Node = nil
	p := this.Root
	deleteLeft := true

	// 1.找到此待删除节点
	for p != nil {
		compareRes := this.CompareFun(v, p.Data)
		if compareRes < 0 {
			pp = p
			deleteLeft = true
			p = p.LeftNode
		} else if compareRes > 0 {
			pp = p
			deleteLeft = false
			p = p.RightNode
		} else {
			break
		}
	}

	if p == nil {
		return false
	}

	if p.LeftNode == nil && p.RightNode == nil {
		// 删除叶子节点
		if pp == nil {
			// 删除根节点
			this.Root = nil
			return true
		}

		// 删除是非根节点,且无叶子节点
		if deleteLeft {
			pp.LeftNode = nil
		} else {
			pp.RightNode = nil
		}
	} else if p.RightNode != nil {
		// 删除的节点有右子节点
		// ① 找到右子树的最小左节点
		pq := p
		q := p.RightNode
		deleteLeftInside := false

		for q.LeftNode != nil {
				pq = q
				deleteLeftInside = true
				q = q.LeftNode
		}

		// ② 删除此节点,最小节点
		if deleteLeftInside {
			pq.LeftNode = nil
		} else {
			pq.RightNode = nil
		}

		// ③ 把待删除的左右节点,追加到最小节点后
		q.LeftNode = p.LeftNode
		q.RightNode = p.RightNode

		// ④ 最小节点,替换掉待删除节点
		if pp == nil {
			this.Root = q
			return true
		}

		if deleteLeft {
			pp.LeftNode = q
		} else {
			pp.RightNode = q
		}
	} else {
		// 删除的节点仅有左节点
		if pp == nil {
			// 删除根节点
			this.Root = p.LeftNode
			return true
		}

		if deleteLeft {
			pp.LeftNode = p.LeftNode
		} else {
			pp.RightNode = p.LeftNode
		}
	}
	return true
}

func main()  {

	bst := NewBst(3, CompareFun)
	bst.Insert(2)
	bst.Insert(1)
	bst.Insert(7)
	bst.Insert(6)
	bst.Insert(5)
	bst.Insert(8)

	bst.InOrder()
	bst.Delete(2)
	bst.InOrder()

	fmt.Println(bst)
}

时间复杂度

从代码实现上看,二叉查找树的查找,删除,插入操作的时间复杂度,都是跟树的高度有关,跟树的高度成正比。也就是O(height)。

树的高度就等于最大层数减一,为了方便计算,我们转换成层来表示。从图中可以看出,包含 n 个节点的完全二叉树中,第一层包含 1 个节点,第二层包含 2 个节点,第三层包含 4 个节点,依次类推,下面一层节点个数是上一层的 2 倍,第 K 层包含的节点个数就是 2^(K-1)。不过,对于完全二叉树来说,最后一层的节点个数有点儿不遵守上面的规律了。它包含的节点个数在 1 个到 2^(L-1) 个之间(我们假设最大层数是 L)。如果我们把每一层的节点个数加起来就是总的节点个数 n。也就是说,如果节点的个数是 n,那么 n 满足这样一个关系:

n >= 1+2+4+8+...+2^(L-2)+1

n <= 1+2+4+8+...+2^(L-2)+2^(L-1)

借助等比数列的求和公式,我们可以计算出,L 的范围是 [log2(n+1), log2n +1]。完全二叉树的层数小于等于 log2n +1,也就是说,完全二叉树的高度小于等于 log2n。显然,极度不平衡的二叉查找树,它的查找性能肯定不能满足我们的需求。我们需要构建一种不管怎么删除、插入数据,在任何时候,都能保持任意节点左右子树都比较平衡的二叉查找树:如“AVL树”,或者不是要求严格平衡的“红黑树“,这些平衡二叉查找树的高度接近 logn,所以插入、删除、查找操作的时间复杂度也比较稳定,是 O(logn)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值