Go 构建二叉搜索树

80 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

Go 构建二叉搜索树

算法描述:二叉搜索树,按照一个节点的左子叶小于节点的值,右子叶大于节点的值搜索二叉树,插入和搜索的时间复杂度是 O(lgN)。

二叉搜索树(binary search tree, BST),根节点比左边子树的所有节点都大,比右边子树上的所有节点都小。 )

算法实现

结构体定义

// Node 定义节点.
type Node struct {
	value int   // 因为目前Go的泛型还没有发布,所以我们这里以一个int具体类型为例
	left  *Node // 左子节点
	right *Node // 右子节点
}
// BST 是一个节点的值为int类型的二叉搜索树.
type BST struct {
	root *Node
}

插入树

往一棵树上增加节点, 这个使用递归方式实现。 如果当前节点比根节点小,那么从左节点开始找,否则从右节点开始找,如果为空,则将节点放到对应位置上。

// Insert 插入一个元素.
func (bst *BST) Insert(value int) {
	newNode := &Node;{value, nil, nil}
	// 如果二叉树为空,那么这个节点就当作跟节点
	if bst.root == nil {
		bst.root = newNode
	} else {
		insertNode(bst.root, newNode)
	}
}
// 从根节点依次比较
func insertNode(root, newNode *Node) {
	if newNode.value < root.value { // 应该放到根节点的左边
		if root.left == nil {
			root.left = newNode
		} else {
			insertNode(root.left, newNode)
		}
	} else if newNode.value > root.value { // 应该放到根节点的右边
		if root.right == nil {
			root.right = newNode
		} else {
			insertNode(root.right, newNode)
		}
	}
	// 否则等于根节点
}

删除节点

还是以递归的方式,先从左边找,再从右边找,删除节点,有提升节点操作,如果找到了删除节点,如果删除节点左右节点都为空,那么这个节点设置为空即可,如果当前节点 左节点为空,那么将当前节点的右节点提升为当前节点,如果当前节点 的右节点为空,将当前节点的左节点提升为当前节点。 如果左右节点都不为空,从右边节点找到一个最小的节点,或者从左子树中找到一个最大的节点进行提升。并且将这个节点从右子树或者左子树中删除

// Remove 删除一个元素.
func (bst *BST) Remove(value int) bool {
	_, existed := remove(bst.root, value)
	return existed
}
// 用来递归移除节点的辅助方法.
// 返回替换root的新节点,以及元素是否存在
func remove(root *Node, value int) (*Node, bool) {
	if root == nil {
		return nil, false
	}
	var existed bool
	// 从左边找
	if value < root.value {
		root.left, existed = remove(root.left, value)
		return root, existed
	}
	// 从右边找
	if value > root.value {
		root.right, existed = remove(root.right, value)
		return root, existed
	}
	// 如果此节点正是要移除的节点,那么返回此节点,同时返回之前可能需要调整.
	existed = true
	// 如果此节点没有孩子,直接返回即可
	if root.left == nil && root.right == nil {
		root = nil
		return root, existed
	}
	// 如果左子节点为空, 提升右子节点
	if root.left == nil {
		root = root.right
		return root, existed
	}
	// 如果右子节点为空, 提升左子节点
	if root.right == nil {
		root = root.left
		return root, existed
	}
	// 如果左右节点都存在,那么从右边节点找到一个最小的节点提升,这个节点肯定比左子树所有节点都大.
	// 也可以从左子树节点中找一个最大的提升,道理一样.
	smallestInRight, _ := min(root.right)
	// 提升
	root.value = smallestInRight
	// 从右边子树中移除此节点
	root.right, _ = remove(root.right, smallestInRight)
	return root, existed
}

搜索树

所谓搜索,其实就是查询当前节点值是否存在,搜索节点比跟节点小,从左搜索,否则从右搜索,直到搜索到位置

// Search 搜索元素(检查元素是否存在)
func (bst *BST) Search(value int) bool {
	return search(bst.root, value)
}
func search(n *Node, value int) bool {
	if n == nil {
		return false
	}
	if value < n.value {
		return search(n.left, value)
	}
	if value > n.value {
		return search(n.right, value)
	}
	return true
}

搜索树的最大最小值

最小值在最左边搜索,最大值,在树最右边。

// Min 二叉搜索树中的最小值
func (bst *BST) Min() (int, bool) {
	return min(bst.root)
}
func min(node *Node) (int, bool) {
	if node == nil {
		return 0, false
	}
	n := node
	// 从左边找
	for {
		if n.left == nil {
			return n.value, true
		}
		n = n.left
	}
}
// Max 二叉搜索树中的最大值
func (bst *BST) Max() (int, bool) {
	return max(bst.root)
}
func max(node *Node) (int, bool) {
	if node == nil {
		return 0, false
	}
	n := node
	// 从右边找
	for {
		if n.right == nil {
			return n.value, true
		}
		n = n.right
	}
}

遍历搜索树

二叉树遍历右中序,后序,前序遍历, 也可递归是实现。

// PreOrderTraverse 前序遍历
func (bst *BST) PreOrderTraverse(f func(int)) {
	preOrderTraverse(bst.root, f)
}
func preOrderTraverse(n *Node, f func(int)) {
	if n != nil {
		f(n.value) // 前
		preOrderTraverse(n.left, f)
		preOrderTraverse(n.right, f)
	}
}
// PostOrderTraverse 后序遍历
func (bst *BST) PostOrderTraverse(f func(int)) {
	postOrderTraverse(bst.root, f)
}
func postOrderTraverse(n *Node, f func(int)) {
	if n != nil {
		postOrderTraverse(n.left, f)
		postOrderTraverse(n.right, f)
		f(n.value) // 后
	}
}

参考资料