Estructura de datos de Golang: árbol de búsqueda binaria

Implementación y operaciones comunes del árbol de búsqueda binaria en Golang, serie de estructura de datos Texto original: flaviocopes.com .

concepto

Árbol: una estructura de datos jerárquica, análoga a un árbol genealógico.

Árbol binario: un árbol en el que cada nodo tiene como máximo 2 nodos secundarios

Árbol de búsqueda binaria: un árbol binario en el que el valor del nodo izquierdo es menor que el valor del nodo derecho

  • Profundidad: la longitud de la ruta única desde el nodo raíz hasta el nodo actual

  • Altura: la longitud del camino más largo desde el nodo actual hasta una hoja

  • Raíz: un nodo de árbol con una profundidad de 0

  • Nodo interno: un nodo con al menos un nodo secundario

  • Hoja: un nodo sin hijos.

  • Nodo hermano (hermano): nodos secundarios que tienen el mismo nodo principal

[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-sanguijuelas, se recomienda guardar la imagen y cargarla directamente (img-qZNOjHZ4-1689064644667)(https://contents.yinzige.com/concept.png )]

árbol de búsqueda binaria

Operaciones comunes y definiciones de nodos.

Insert(v)		// 向二叉搜索树的合适位置插入节点
Search(k)		// 检查序号为 k 的元素在树中是否存在
Remove(v)		// 移除树中所有值为 v 的节点
Min()			// 获取二叉搜索树中最小的值
Max()			// 获取二叉搜索树中最大的值
InOrderTraverse()	// 中序遍历树
PreOrderTraverse()	// 先序遍历树
PostOrderTraverse()	// 后续遍历树
String()		// 在命令行格式化打印出二叉树

También use genny para proporcionar reutilización del código, el tipo de árbol se denomina: ItemBinarySearchTreey la estructura del nodo del árbol se define de la siguiente manera:

type Node struct {
    
    
	key   int	// 中序遍历的节点序号
	value Item	// 节点存储的值
	left  *Node	// 左子节点
	right *Node	// 右子节点
}

La clave es el número de serie de la posición de cada nodo en el recorrido de pedido previo. El valor de la clave aquí es int, que puede ser cualquier tipo de datos comparable.

Inserción y recorrido

La operación de inserción debe utilizar recursividad, y la operación de inserción debe encontrar la posición adecuada del nuevo nodo en el árbol de arriba a abajo: si el valor del nuevo nodo es menor que cualquier nodo, continúe buscando a la izquierda subárbol y, de manera similar, busque el subárbol derecho hasta que encuentre Vaya al nodo hoja e insértelo nuevamente.

Hay tres modos de operaciones transversales:

  • Recorrido en orden (en orden): subárbol izquierdo -> nodo raíz -> árbol derecho:1->2->3->4->5->6->7->8->9->10->11

  • Recorrido de pedido anticipado (pedido anticipado): nodo raíz -> subárbol izquierdo -> subárbol derecho:8->4->2->1->3->6->5->7 >10->9->11

  • Recorrido posterior al orden (post-orden): subárbol izquierdo -> subárbol derecho -> nodo raíz:1->3->2->5->7->6->4->9->11->10->8


Código

Insertar

// 向二叉搜索树的合适位置插入节点
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)
		}
	}
}

Buscar

// 检查序号为 k 的元素在树中是否存在
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 // key == node.key
}

Eliminar

El proceso de eliminación de nodos.

Primero busque recursivamente y luego elimine el nodo. Sin embargo, al eliminarlo, es necesario dividirlo en las siguientes tres situaciones según la cantidad de nodos secundarios que posee el nodo:

[Error en la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-m2ENIRMb-1689064644669)(https://contents.yinzige.com/remove-node .png)]

Código
// 删除指定序号的节点
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 key == node.key {
    
    
	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
	}

	// 要删除的节点有 2 个子节点,找到右子树的最左节点,替换当前节点
	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
}

Mínimo máximo

// 获取树中值最小的节点:最左节点
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
	}
}

atravesar

// 先序遍历:根节点 -> 左子树 -> 右子树
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) // 最后打印右子树
	}
}

Cadena

// 后序遍历打印树结构
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)
}

Resumir

returnPara el funcionamiento del árbol de búsqueda binaria, agregar, eliminar y verificar están relacionados con la recursividad, por lo que la condición de terminación de la recursividad debe analizarse claramente durante la implementación y se puede evitar el bucle infinito en las condiciones correctas.

Supongo que te gusta

Origin blog.csdn.net/qq_24694139/article/details/131663664
Recomendado
Clasificación