Árbol de búsqueda binario (buscar, insertar, borrar, explicar y realizar + imágenes y textos)

Tabla de contenido

1. Árbol de búsqueda binaria (BST)

  1.1 Concepto de árbol de búsqueda binario

  1.2 Operación de árbol de búsqueda binaria

        1.2.1 Buscar en el árbol de búsqueda binaria

        1.2.2 Inserción de árbol de búsqueda binaria 

        1.2.3 Eliminación del árbol de búsqueda binaria

2. Implementación del árbol de búsqueda binaria

  2.1 Estructura básica de BST

  2.2 Función miembro de operación BST (no recursiva)

  2.3 Función miembro de operación BST (recursividad)

3. Aplicación del árbol de búsqueda binaria

4. Análisis de rendimiento del árbol de búsqueda binaria


1. Árbol de búsqueda binaria (BST)

  1.1 Concepto de árbol de búsqueda binario

Un árbol de búsqueda binario, también conocido como árbol de clasificación binario, es un árbol vacío o un árbol binario con las siguientes propiedades:

  • Si su subárbol izquierdo no está vacío, los valores de todos los nodos en el subárbol izquierdo son menores que el valor del nodo raíz
  • Si su subárbol derecho no está vacío, los valores de todos los nodos en el subárbol derecho son mayores que el valor del nodo raíz
  • Sus subárboles izquierdo y derecho también son árboles de búsqueda binaria.

Permítanme dar algunos ejemplos para ver la estructura de manera más intuitiva:

 

  1.2 Operación de árbol de búsqueda binaria

int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};

        1.2.1 Buscar en el árbol de búsqueda binaria

  • Comience a comparar y busque desde la raíz, si es más grande que la raíz, vaya a la derecha para buscar, y si es más pequeña que la raíz, vaya a la izquierda para buscar.
  • La altura se busca la mayoría de las veces , y el valor no existe si está vacío y no se ha encontrado.

        1.2.2 Inserción de árbol de búsqueda binaria 

El proceso específico de inserción es el siguiente:

  • Si el árbol está vacío, agregue un nodo directamente y asígnelo al puntero raíz
  • El árbol no está vacío, busque la posición de inserción de acuerdo con la naturaleza del árbol de búsqueda binaria e inserte un nuevo nodo (registre el nodo principal y juzgue si el nodo insertado debe estar en el subárbol izquierdo o en el subárbol derecho del padre). nodo) 

        1.2.3 Eliminación del árbol de búsqueda binaria

Primero averigüe si el elemento está en el árbol de búsqueda binaria, si no, devuélvalo, de lo contrario, el nodo que se eliminará se puede dividir en las siguientes cuatro situaciones
condición:
        a. El nodo que se eliminará no tiene nodos secundarios
        b. El nodo que se eliminará es solo el nodo secundario izquierdo
        c. El nodo que se eliminará es solo el nodo secundario derecho
        D. El nodo que se eliminará tiene nodos secundarios izquierdo y derecho.

Parece que hay 4 casos de eliminación de nodos, pero en realidad a, b y c se pueden fusionar, por lo que solo hay 2 casos:

        a: El nodo que se eliminará no tiene hijos/solo un hijo: elimine el nodo y haga que el nodo principal apunte al nodo secundario del nodo eliminado (ningún elemento secundario se considera un elemento secundario es un nodo vacío, solo apunte a cualquiera )

        b: El nodo que se eliminará tiene hijos izquierdo y derecho: use el método de reemplazo para encontrar el nodo más pequeño en el subárbol derecho del nodo eliminado (el nodo más a la izquierda en el subárbol derecho), y reemplace el valor del nodo más pequeño con el valor del nodo eliminado. Luego, elimine el nodo más pequeño (en este momento, el nodo más pequeño no tiene hijos o solo tiene un hijo, y se puede eliminar directamente si cumple la condición a)

2. Implementación del árbol de búsqueda binaria

  2.1 Estructura básica de BST


Nodo:

template<class K>
struct BSTreeNode
{
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	K _key;

	BSTreeNode(const K& key)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{}
};

árbol BST:

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
    //成员函数
private:
    Node* _root=nullptr;
};

Encontrar:

bool Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
        //待查值大于当前结点,去右子树
		if (cur->_key < key)
		{
			cur = cur->_right;
		}
        //待查值小于当前结点,去左子树
		else if (cur->_key > key)
		{
			cur = cur->_left;
		}
        //找到
		else
		{
			return true;
		}
	}

	return false;
}

  2.2 Función miembro de operación BST (no recursiva)


insertar:

bool Insert(const K& key)
{
    //树为空,则直接新增结点,赋值给_root指针
	if (_root == nullptr)
	{
		_root = new Node(key);
		return true;
	}

	Node* parent = nullptr;
	Node* cur = _root;
    //按性质查找插入的位置,并且记录父结点
	while (cur)
	{
		if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
        //已有结点,不需要再插入
		else
		{
			return false;
		}
	}

	cur = new Node(key);
    //判断是插入父结点的左部还是右部
	if (parent->_key < key)
	{
		parent->_right = cur;
	}
	else
	{
		parent->_left = cur;
	}

	return true;
}

borrar:

bool Erase(const K& key)
{
	Node* parent = nullptr;
	Node* cur = _root;
    //查找是否有待删除的节点
	while (cur)
	{
		if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			// 开始删除
			// 1、左为空
			// 2、右为空
			// 3、左右都不为空
			if (cur->_left == nullptr)
			{
                //判断下当前节点是否是_root,若是,无法用parent(当前为nullptr,防止野指针错误)
				if (cur == _root)
				{
					_root = cur->_right;
				}
				else
				{
					if (cur == parent->_left)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
				}

				delete cur;
				cur = nullptr;
			}
			else if (cur->_right == nullptr)
			{
				if (_root == cur)
				{
					_root = cur->_left;
				}
				else
				{
					if (cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}

				delete cur;
				cur = nullptr;
			}
			else
			{
				//记录删除节点父节点
				Node* minParent = cur;
				//找到右子树最小节点进行替换
				Node* min = cur->_right;
				while (min->_left)
				{
					minParent = min;
					min = min->_left;
				}
				swap(cur->_key, min->_key);
				//min在父的左孩子上
				if (minParent->_left == min)
					//万一最左节点还有右孩子节点,或者是叶子也直接指右为空
					minParent->_left = min->_right;
				//min在父的右孩子上(待删除节点在根节点,最左节点为根节点的右孩子)
				else
					minParent->_right = min->_right;
				delete min;
				min == nullptr;
			}
			return true;
		}
	}
	return false;
}

Otras funciones de miembros no se mostrarán aquí, aquí hay otro pequeño consejo:

predeterminado: fuerza al compilador a generar la estructura predeterminada: el uso de C++ 11

BSTree()=default;

  2.3 Función miembro de operación BST (recursividad)

Todavía es recursivo, y si comprende el no recursivo anterior, puede transformarlo en recursivo. 


Encontrar:

bool _FindR(Node*& root, const K& key)
{
	if (root == nullptr)
		return false;
	if (root->_key < key)
	{
		return _FindR(root->_right, key);
	}
	else if (root->_key > key)
	{
		return _FindR(root->_left, key);
	}
	else
	{
		return true;
	}
}

insertar:

bool _InsertR(Node*& root, const K& key)
{
	if (root == nullptr)
	{
		root = new Node(key);
		return true;
	}

	if (root->_key < key)
		return _InsertR(root->_right, key);
	else if (root->_key > key)
		return _InsertR(root->_left, key);
	else
		return false;
}

borrar:

bool _EraseR(Node* root, const K& key)
{
	Node* del = root;
	if (root == nullptr)
		return false;
	if (root->_key < key)
		return _EraseR(root->_right, key);
	else if (root->_key > key)
		return _EraseR(root->_left, key);
	else
	{
		if (root->_left == nullptr)
			root = root->_right;
		else if (root->_right == nullptr)
			root = root->_left;
		else
		{
			//找右数的最左节点替换删除
			Node* min = root->_right;
			while (min->_left)
			{
				min = min->_left;
			}
			swap(root->_key, min->_key);
			//交换后结构改变不是搜索二叉树了,规定范围在右树(因为是右树最左节点替换)再递归
			return _EraseR(root->_right, key); 
		}
		delete del;
		return true;
				
	}

}

3. Aplicación del árbol de búsqueda binaria

1. Modelo K : el modelo K tiene solo la clave como código clave, y solo la clave debe almacenarse en la estructura, y el código clave es el valor que se busca.
Por ejemplo: dada una palabra palabra, juzgue si la palabra está escrita correctamente , el método específico es el siguiente:
Construya un árbol de búsqueda binaria con cada palabra en el conjunto de todas las palabras en el tesauro como clave
Recuperar si la palabra existe en el árbol de búsqueda binaria, si existe la ortografía es correcta, si no existe la ortografía es incorrecta.
2. Modelo KV : Cada código clave tiene un valor correspondiente Valor, es decir, un par clave-valor de <Clave, Valor>. Este método
La fórmula es muy común en la vida real:
Por ejemplo, el diccionario inglés-chino es la relación correspondiente entre inglés y chino . Puede encontrar rápidamente el chino correspondiente a través del inglés. Una palabra en inglés y su correspondiente chino <palabra, chino> constituyen un par clave-valor;
Otro ejemplo es contar el número de palabras . Después de que las estadísticas sean exitosas, puede encontrar rápidamente el número de ocurrencias de una palabra dada. La palabra y el número de ocurrencias son <word, count> para formar un par clave-valor.

4. Análisis de rendimiento del árbol de búsqueda binaria

Tanto las operaciones de inserción como las de eliminación deben buscarse primero, y la eficiencia de la búsqueda representa el rendimiento de cada operación en el árbol de búsqueda binaria.

Para un árbol de búsqueda binario con n nodos, si la probabilidad de buscar cada elemento es igual, la longitud de búsqueda promedio del árbol de búsqueda binario es una función de la profundidad del nodo en el árbol de búsqueda binario, es decir, cuanto más profundo sea el nodo, la comparación Las veces más.

Pero para el mismo conjunto de códigos clave, si el orden de inserción de cada código clave es diferente, se puede obtener un árbol de búsqueda binario con una estructura diferente:

En el caso óptimo : el árbol binario de búsqueda es un árbol binario completo (o casi un árbol binario completo), y su número medio de comparaciones es: log(N)

En el peor de los casos : el árbol de búsqueda binaria degenera en un árbol de una sola rama (o similar a una sola rama), y su número promedio de comparaciones es N

 Si degenera en un árbol de una sola rama, se pierde el rendimiento del árbol de búsqueda binaria. ¿Se puede mejorar eso? No importa en qué orden se inserten los códigos clave, ¿puede ser óptimo? Esto requiere árboles AVL y árboles rojo-negro.

Supongo que te gusta

Origin blog.csdn.net/bang___bang_/article/details/130869356
Recomendado
Clasificación