Introducción al árbol binario: tutoriales de algoritmos y estructura de datos
Árbol binario es un estructura de datos no lineal donde cada nodo tiene como máximo dos hijos. En este artículo, cubriremos todos los conceptos básicos de Binary Tree, operaciones en Binary Tree, su implementación, ventajas y desventajas que lo ayudarán a resolver todos los problemas basados en Binary Tree.
Tabla de contenidos
- ¿Qué es el árbol binario?
- Representación del árbol binario
- Tipos de árbol binario
- Operaciones en árbol binario
- Operaciones auxiliares en árbol binario
- Implementación del árbol binario
- Análisis de complejidad de las operaciones del árbol binario
- Ventajas del árbol binario
- Desventajas del árbol binario
- Aplicaciones del árbol binario
- Preguntas frecuentes sobre el árbol binario
¿Qué es el árbol binario?
El árbol binario es un estructura de datos de árbol (no lineal) en el que cada nodo puede tener como mucho dos hijos a los que se hace referencia como el niño abandonado y el niño correcto .
El nodo superior en un árbol binario se llama raíz , y los nodos más inferiores se llaman hojas . Un árbol binario se puede visualizar como una estructura jerárquica con la raíz en la parte superior y las hojas en la parte inferior.
Representación del árbol binario
Cada nodo de un árbol binario tiene tres partes:
- Datos
- Puntero al niño izquierdo
- Puntero al niño correcto
A continuación se muestra la representación de un Nodo del Árbol Binario en diferentes idiomas:
C++ // Use any below method to implement Nodes of tree // Method 1: Using 'struct' to make // user-define data type struct node { int data; struct node* left; struct node* right; }; // Method 2: Using 'class' to make // user-define data type class Node { public: int data; Node* left; Node* right; }; C // Structure of each node of the tree struct node { int data; struct node* left; struct node* right; }; Java // Class containing left and right child // of current node and key value class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } Pitón # A Python class that represents # an individual node in a Binary Tree class Node: def __init__(self, key): self.left = None self.right = None self.val = key
C# // Class containing left and right child // of current node and key value class Node { int key; Node left, right; public Node(int item) { key = item; left = right = null; } } javascript
Tipos de árbol binario
El árbol binario se puede clasificar en varios tipos según múltiples factores:
- Según el número de hijos
- Árbol binario completo
- Árbol binario degenerado
- Árboles binarios sesgados
- Sobre la base de la finalización de niveles.
- Árbol binario completo
- Árbol binario perfecto
- Árbol binario equilibrado
- Sobre la base de los valores de nodo:
- Árbol de búsqueda binaria
- Árbol AVL
- árbol negro rojo
- árbol B
- Árbol B+
- Árbol de segmentos
Operaciones en árbol binario
1. Inserción en árbol binario
Podemos insertar un nodo en cualquier lugar de un árbol binario insertando el nodo como hijo izquierdo o derecho de cualquier nodo o haciendo que el nodo sea la raíz del árbol.
Algoritmo para insertar un nodo en un Árbol Binario:
- Compruebe si hay un nodo en el árbol binario al que le falta el hijo izquierdo. Si dicho nodo existe, inserte el nuevo nodo como su hijo izquierdo.
- Compruebe si hay un nodo en el árbol binario al que le falta el hijo derecho. Si dicho nodo existe, inserte el nuevo nodo como su hijo derecho.
- Si no encontramos ningún nodo al que le falte el hijo izquierdo o derecho, entonces busque el nodo al que le faltan ambos hijos e inserte el nodo como su hijo izquierdo o derecho.
2. Recorrido del árbol binario
El recorrido del árbol binario implica visitar todos los nodos del árbol binario. Los algoritmos de recorrido de árbol se pueden clasificar en términos generales en dos categorías:
- Algoritmos de búsqueda en profundidad (DFS)
- Algoritmos de búsqueda en amplitud (BFS)
Algoritmos de búsqueda en profundidad (DFS):
- Recorrido de pedido anticipado (actual-izquierda-derecha): Visite el nodo actual antes de visitar cualquier nodo dentro de los subárboles izquierdo o derecho. Aquí, el recorrido es raíz – hijo izquierdo – hijo derecho. Significa que primero se atraviesa el nodo raíz, luego su hijo izquierdo y finalmente el hijo derecho.
- Recorrido en orden (izquierda-actual-derecha): Visite el nodo actual después de visitar todos los nodos dentro del subárbol izquierdo pero antes de visitar cualquier nodo dentro del subárbol derecho. Aquí, el recorrido es hijo izquierdo – raíz – hijo derecho. Significa que primero se atraviesa el hijo izquierdo, luego su nodo raíz y finalmente el hijo derecho.
- Recorrido posterior al orden (izquierda-derecha-actual): Visite el nodo actual después de visitar todos los nodos de los subárboles izquierdo y derecho. Aquí, el recorrido es hijo izquierdo – hijo derecho – raíz. Significa que el hijo izquierdo ha atravesado primero, luego el hijo derecho y finalmente su nodo raíz.
Algoritmos de búsqueda en amplitud (BFS):
- Recorrido de orden de nivel: Visite los nodos nivel por nivel y de izquierda a derecha en el mismo nivel. Aquí, el recorrido es por niveles. Significa que el niño más a la izquierda ha atravesado primero y luego los otros niños del mismo nivel de izquierda a derecha han atravesado.
3. Eliminación en el árbol binario
Podemos eliminar cualquier nodo en el árbol binario y reorganizar los nodos después de eliminarlos para formar nuevamente un árbol binario válido.
Algoritmo para eliminar un nodo en un Árbol Binario:
- Comenzando en la raíz, busque el nodo más profundo y más a la derecha en el árbol binario y el nodo que queremos eliminar.
- Reemplace los datos del nodo más profundo a la derecha con el nodo que se va a eliminar.
- Luego elimine el nodo más profundo de la derecha.
4. Búsqueda en árbol binario
Podemos buscar un elemento en el nodo utilizando cualquiera de las técnicas transversales.
Algoritmo para buscar un nodo en un Árbol Binario:
- Comience desde el nodo raíz.
- Compruebe si el valor del nodo actual es igual al valor objetivo.
- Si el valor del nodo actual es igual al valor objetivo, entonces este nodo es el nodo requerido.
- De lo contrario, si el valor del nodo no es igual al valor objetivo, comience la búsqueda en el hijo izquierdo y derecho.
- Si no encontramos ningún nodo cuyo valor sea igual al objetivo, entonces el valor no está presente en el árbol.
Operaciones auxiliares en árbol binario
- Encontrar la altura del árbol.
- Encuentra el nivel de un nodo en un árbol binario
- Encontrar el tamaño de todo el árbol.
Implementación del árbol binario
A continuación se muestra el código para la inserción, eliminación y recorrido del árbol binario:
C #include // Node structure to define the structure of the node typedef struct Node { int data; struct Node *left, *right; } Node; // Function to create a new node Node* newNode(int val) { Node* temp = (Node*)malloc(sizeof(Node)); temp->datos = valor; temperatura->izquierda = temperatura->derecha = NULL; temperatura de retorno; } // Función para insertar nodos Node* insert(Node* root, int data) { // Si el árbol está vacío, el nuevo nodo se convierte en la raíz if (root == NULL) { root = newNode(data); devolver raíz; } // Cola para recorrer el árbol y encontrar la posición para insertar el nodo Node* queue[100]; int frente = 0, trasero = 0; cola[trasero++] = raíz; while (frente! = trasero) { Nodo* temp = cola[frente++]; // Insertar nodo como hijo izquierdo del nodo padre if (temp->left == NULL) { temp->left = newNode(data); romper; } // Si el hijo izquierdo no es nulo, envíelo a la cola else queue[rear++] = temp->left; // Insertar nodo como hijo derecho del nodo padre if (temp->right == NULL) { temp->right = newNode(data); romper; } // Si el hijo derecho no es nulo, envíelo a la cola else queue[rear++] = temp->right; } devolver raíz; } /* Función para eliminar el nodo más profundo dado (d_node) en el árbol binario */ void deletDeepest(Node* root, Node* d_node) { Node* queue[100]; int frente = 0, trasero = 0; cola[trasero++] = raíz; // Realiza un recorrido de orden de nivel hasta el último nodo Node* temp; while (frente! = trasero) { temp = cola[frente++]; if (temp == d_node) { temp = NULL; libre(d_nodo); devolver; } if (temp->derecha) { if (temp->derecha == d_node) { temp->derecha = NULL; libre(d_nodo); devolver; } else cola[trasero++] = temp->derecha; } if (temp->izquierda) { if (temp->izquierda == d_node) { temp->izquierda = NULL; libre(d_nodo); devolver; } else cola[trasero++] = temp->izquierda; } } } /* Función para eliminar elemento en el árbol binario */ Eliminación de nodo*(Nodo* raíz, int clave) { if (!root) return NULL; if (raíz->izquierda == NULL && raíz->derecha == NULL) { if (raíz->datos == clave) return NULL; de lo contrario devolverá la raíz; } Nodo* cola[100]; int frente = 0, trasero = 0; cola[trasero++] = raíz; Temperatura del nodo*; Nodo* key_node = NULL; // Realice un recorrido de orden de nivel para encontrar el nodo más profundo (temp) y el nodo que se eliminará (key_node) while (front != rear) { temp = queue[front++]; if (temp->datos == clave) key_node = temp; if (temp->izquierda) cola[trasero++] = temp->izquierda; if (temp->derecha) cola[trasero++] = temp->derecha; } if (key_node! = NULL) { int x = temp->datos; key_node->datos = x; eliminarMás profundo(raíz, temporal); } devolver raíz; } // Recorrido del árbol en orden (Izquierda - Raíz - Derecha) void inorderTraversal(Nodo* raíz) { if (!root) return; inorderTraversal(raíz->izquierda); printf('%d ', raíz->datos); inorderTraversal(raíz->derecha); } // Recorrido del árbol de pedidos anticipados (Raíz - Izquierda - Derecha) void preorderTraversal(Nodo* raíz) { if (!root) return; printf('%d ', raíz->datos); preordenTraversal(raíz->izquierda); preordenTraversal(raíz->derecha); } // recorrido del árbol de postorden (Izquierda - Derecha - Raíz) void postorderTraversal(Node* root) { if (root == NULL) return; postorderTraversal(raíz->izquierda); postorderTraversal(raíz->derecha); printf('%d ', raíz->datos); } // Función para el recorrido del árbol de orden de niveles void levelorderTraversal(Node* root) { if (root == NULL) return; // Cola para recorrido de orden de nivel Nodo* cola[100]; int frente = 0, trasero = 0; cola[trasero++] = raíz; while (frente! = trasero) { Nodo* temp = cola[frente++]; printf('%d ', temperatura->datos); // Empujar al hijo izquierdo en la cola if (temp->left) queue[rear++] = temp->left; // Empuja al hijo derecho en la cola if (temp->right) queue[rear++] = temp->right; } } /* Función del controlador para comprobar el algoritmo anterior. */ int main() { Nodo* raíz = NULL; // Inserción de nodos root = insert(root, 10); raíz = insertar(raíz, 20); raíz = insertar(raíz, 30); raíz = insertar(raíz, 40); printf('Recorrido de preorden: '); preordenTraversal(raíz); printf('
Recorrido en orden: '); inorderTraversal(raíz); printf('
Recorrido posterior al pedido: '); postorderTraversal(raíz); printf('
Recorrido de orden de niveles: '); nivelordenTraversal(raíz); // Eliminar el nodo con datos = 20 root = deletion(root, 20); printf('
Recorrido en orden después de la eliminación: '); inorderTraversal(raíz); devolver 0; } Java import java.util.LinkedList; import java.util.Queue; // Node class to define the structure of the node class Node { int data; Node left, right; // Parameterized Constructor Node(int val) { data = val; left = right = null; } } public class BinaryTree { // Function to insert nodes public static Node insert(Node root, int data) { // If tree is empty, new node becomes the root if (root == null) { root = new Node(data); return root; } // Queue to traverse the tree and find the position to // insert the node Queue q = nueva ListaEnlazada(); q.oferta(raíz); while (!q.isEmpty()) { Temperatura del nodo = q.poll(); // Insertar nodo como hijo izquierdo del nodo padre if (temp.left == null) { temp.left = new Node(data); romper; } // Si el hijo izquierdo no es nulo, envíelo a la // cola else q.offer(temp.left); // Insertar nodo como hijo derecho del nodo padre if (temp.right == null) { temp.right = new Node(data); romper; } // Si el hijo correcto no es nulo, envíelo a la // cola else q.offer(temp.right); } devolver raíz; } /* función para eliminar el nodo más profundo dado (d_node) en el árbol binario */ public static void deletDeepest(Node root, Node d_node) { Cola q = nueva ListaEnlazada(); q.oferta(raíz); // Realiza un recorrido de orden de nivel hasta el último nodo Node temp; mientras (!q.isEmpty()) { temp = q.poll(); if (temp == d_node) { temp = nulo; d_nodo = nulo; devolver; } if (temp.right! = nulo) { if (temp.right == d_node) { temp.right = null; d_nodo = nulo; devolver; } else q.oferta(temp.derecha); } if (temp.izquierda! = nulo) { if (temp.izquierda == d_node) { temp.izquierda = nulo; d_nodo = nulo; devolver; } else q.oferta(temp.izquierda); } } } /* función para eliminar elemento en el árbol binario */ eliminación de nodo estático público (raíz del nodo, clave int) { if (root == null) return null; if (root.left == null && root.right == null) { if (root.data == clave) return null; de lo contrario devolverá la raíz; } Cola q = nueva ListaEnlazada(); q.oferta(raíz); Temperatura del nodo = nuevo nodo (0); Nodo key_node = nulo; // Realice un recorrido de orden de nivel para encontrar el // nodo más profundo (temp) y el nodo que se eliminará (key_node) while (!q.isEmpty()) { temp = q.poll(); if (temp.data == clave) key_node = temp; if (temp.izquierda! = nulo) q.oferta(temp.izquierda); if (temp.derecho! = nulo) q.oferta(temp.derecho); } if (key_node! = nulo) { int x = temp.data; key_node.datos = x; eliminarMás profundo(raíz, temporal); } devolver raíz; } // Recorrido del árbol en orden (Izquierda - Raíz - Derecha) public static void inorderTraversal(Node root) { if (root == null) return; inorderTraversal(raíz.izquierda); System.out.print(root.data + ' '); inorderTraversal(raíz.derecha); } // Recorrido del árbol de pedidos anticipados (Raíz - Izquierda - Derecha) public static void preorderTraversal(Node root) { if (root == null) return; System.out.print(root.data + ' '); preordenTraversal(root.left); preordenTraversal(root.right); } // recorrido del árbol de postorden (Izquierda - Derecha - Raíz) public static void postorderTraversal(Node root) { if (root == null) return; postorderTraversal(root.left); postorderTraversal(root.right); System.out.print(root.data + ' '); } // Función para el recorrido del árbol de orden de niveles public static void levelorderTraversal(Node root) { if (root == null) return; // Cola para recorrido de orden de nivel Cola q = nueva ListaEnlazada(); q.oferta(raíz); while (!q.isEmpty()) { Temperatura del nodo = q.poll(); System.out.print(temp.data + ' '); // Empujar al hijo izquierdo en la cola if (temp.left != null) q.offer(temp.left); // Empuja al hijo derecho en la cola if (temp.right != null) q.offer(temp.right); } } /* Función del controlador para comprobar el algoritmo anterior. */ public static void main(String[] args) { Raíz del nodo = null; // Inserción de nodos root = insert(root, 10); raíz = insertar(raíz, 20); raíz = insertar(raíz, 30); raíz = insertar(raíz, 40); System.out.print('Recorrido de reserva: '); preordenTraversal(raíz); System.out.print('
Recorrido en orden: '); inorderTraversal(raíz); System.out.print('
Recorrido posterior al pedido: '); postorderTraversal(raíz); System.out.print('
Recorrido de orden de niveles: '); nivelordenTraversal(raíz); // Eliminar el nodo con datos = 20 root = deletion(root, 20); System.out.print('
Recorrido en orden después de la eliminación: '); inorderTraversal(raíz); } } Pitón from collections import deque # Node class to define the structure of the node class Node: def __init__(self, val): self.data = val self.left = self.right = None # Function to insert nodes def insert(root, data): # If tree is empty, new node becomes the root if root is None: root = Node(data) return root # Queue to traverse the tree and find the position to insert the node q = deque() q.append(root) while q: temp = q.popleft() # Insert node as the left child of the parent node if temp.left is None: temp.left = Node(data) break # If the left child is not null push it to the queue else: q.append(temp.left) # Insert node as the right child of parent node if temp.right is None: temp.right = Node(data) break # If the right child is not null push it to the queue else: q.append(temp.right) return root # Function to delete the given deepest node (d_node) in binary tree def deletDeepest(root, d_node): q = deque() q.append(root) # Do level order traversal until last node while q: temp = q.popleft() if temp == d_node: temp = None del d_node return if temp.right: if temp.right == d_node: temp.right = None del d_node return else: q.append(temp.right) if temp.left: if temp.left == d_node: temp.left = None del d_node return else: q.append(temp.left) # Function to delete element in binary tree def deletion(root, key): if not root: return None if root.left is None and root.right is None: if root.data == key: return None else: return root q = deque() q.append(root) temp = None key_node = None # Do level order traversal to find deepest node (temp) and node to be deleted (key_node) while q: temp = q.popleft() if temp.data == key: key_node = temp if temp.left: q.append(temp.left) if temp.right: q.append(temp.right) if key_node is not None: x = temp.data key_node.data = x deletDeepest(root, temp) return root # Inorder tree traversal (Left - Root - Right) def inorderTraversal(root): if not root: return inorderTraversal(root.left) print(root.data, end=' ') inorderTraversal(root.right) # Preorder tree traversal (Root - Left - Right) def preorderTraversal(root): if not root: return print(root.data, end=' ') preorderTraversal(root.left) preorderTraversal(root.right) # Postorder tree traversal (Left - Right - Root) def postorderTraversal(root): if root is None: return postorderTraversal(root.left) postorderTraversal(root.right) print(root.data, end=' ') # Function for Level order tree traversal def levelorderTraversal(root): if root is None: return # Queue for level order traversal q = deque() q.append(root) while q: temp = q.popleft() print(temp.data, end=' ') # Push left child in the queue if temp.left: q.append(temp.left) # Push right child in the queue if temp.right: q.append(temp.right) # Driver function to check the above algorithm if __name__ == '__main__': root = None # Insertion of nodes root = insert(root, 10) root = insert(root, 20) root = insert(root, 30) root = insert(root, 40) print('Preorder traversal:', end=' ') preorderTraversal(root) print('
Inorder traversal:', end=' ') inorderTraversal(root) print('
Postorder traversal:', end=' ') postorderTraversal(root) print('
Level order traversal:', end=' ') levelorderTraversal(root) # Delete the node with data = 20 root = deletion(root, 20) print('
Inorder traversal after deletion:', end=' ') inorderTraversal(root) C# using System; using System.Collections.Generic; // Node class to define the structure of the node public class Node { public int data; public Node left, right; // Parameterized Constructor public Node(int val) { data = val; left = right = null; } } public class Program { // Function to insert nodes public static Node Insert(Node root, int data) { // If tree is empty, new node becomes the root if (root == null) { root = new Node(data); return root; } // Queue to traverse the tree and find the position to insert the node Queue q = nueva cola (); q.En cola (raíz); while (q.Count != 0) { Temperatura del nodo = q.Dequeue(); // Insertar nodo como hijo izquierdo del nodo padre if (temp.left == null) { temp.left = new Node(data); romper; } // Si el hijo izquierdo no es nulo, envíelo a la cola else q.Enqueue(temp.left); // Insertar nodo como hijo derecho del nodo padre if (temp.right == null) { temp.right = new Node(data); romper; } // Si el hijo derecho no es nulo, envíelo a la cola else q.Enqueue(temp.right); } devolver raíz; } /* función para eliminar el nodo más profundo dado (d_node) en el árbol binario */ public static void DeleteDeepest(Node root, Node d_node) { Cola q = nueva cola (); q.En cola (raíz); // Realiza un recorrido de orden de nivel hasta el último nodo Node temp; while (q.Count != 0) { temp = q.Dequeue(); if (temp == d_node) { temp = nulo; d_nodo = nulo; devolver; } if (temp.right! = nulo) { if (temp.right == d_node) { temp.right = null; d_nodo = nulo; devolver; } else q.Enqueue(temp.right); } if (temp.izquierda! = nulo) { if (temp.izquierda == d_node) { temp.izquierda = nulo; d_nodo = nulo; devolver; } más q.Enqueue(temp.left); } } } /* función para eliminar elemento en el árbol binario */ eliminación de nodo estático público (raíz del nodo, clave int) { if (root == null) return null; if (root.left == null && root.right == null) { if (root.data == clave) return null; de lo contrario devolverá la raíz; } Cola q = nueva cola (); q.En cola (raíz); Temperatura del nodo = nuevo nodo (0); Nodo key_node = nulo; // Realice un recorrido de orden de nivel para encontrar el nodo más profundo (temp) y el nodo que se eliminará (key_node) while (q.Count != 0) { temp = q.Dequeue(); if (temp.data == clave) key_node = temp; if (temp.izquierda! = nulo) q.Enqueue(temp.izquierda); if (temp.derecha! = nulo) q.Enqueue(temp.derecha); } if (key_node! = nulo) { int x = temp.data; key_node.datos = x; Eliminar más profundo (raíz, temperatura); } devolver raíz; } // Recorrido del árbol en orden (Izquierda - Raíz - Derecha) public static void InorderTraversal(Node root) { if (root == null) return; InorderTraversal(raíz.izquierda); Consola.Write(root.data + ' '); InorderTraversal(raíz.derecha); } // Recorrido del árbol de pedidos anticipados (Raíz - Izquierda - Derecha) public static void PreorderTraversal(Node root) { if (root == null) return; Consola.Write(root.data + ' '); PreorderTraversal(root.left); PreorderTraversal(root.right); } // recorrido del árbol de postorden (Izquierda - Derecha - Raíz) public static void PostorderTraversal(Node root) { if (root == null) return; PostorderTraversal(root.left); PostorderTraversal(root.right); Consola.Write(root.data + ' '); } // Función para el recorrido del árbol de orden de niveles public static void LevelorderTraversal(Node root) { if (root == null) return; // Cola para recorrido de orden de nivel Cola q = nueva cola (); q.En cola (raíz); while (q.Count != 0) { Temperatura del nodo = q.Dequeue(); Consola.Write(temp.data + ' '); // Empujar al hijo izquierdo en la cola if (temp.left != null) q.Enqueue(temp.left); // Empuja al hijo derecho en la cola if (temp.right != null) q.Enqueue(temp.right); } } /* Función del controlador para comprobar el algoritmo anterior. */ public static void Main(string[] args) { Raíz del nodo = null; // Inserción de nodos root = Insert(root, 10); raíz = Insertar(raíz, 20); raíz = Insertar(raíz, 30); raíz = Insertar(raíz, 40); Console.Write('Recorrido de reserva: '); PreorderTraversal(raíz); Console.Write('
Recorrido en orden: '); InorderTraversal(raíz); Console.Write('
Recorrido posterior al pedido: '); PostorderTraversal(raíz); Console.Write('
Recorrido de orden de niveles: '); LevelorderTraversal(raíz); // Eliminar el nodo con datos = 20 root = Deletion(root, 20); Console.Write('
Recorrido en orden después de la eliminación: '); InorderTraversal(raíz); } } JavaScript // Node class to define the structure of the node class Node { constructor(val) { this.data = val; this.left = null; this.right = null; } } // Function to insert nodes function insert(root, data) { // If tree is empty, new node becomes the root if (root === null) { root = new Node(data); return root; } // queue to traverse the tree and find the position to // insert the node const q = []; q.push(root); while (q.length !== 0) { const temp = q.shift(); // Insert node as the left child of the parent node if (temp.left === null) { temp.left = new Node(data); break; } // If the left child is not null push it to the // queue else q.push(temp.left); // Insert node as the right child of parent node if (temp.right === null) { temp.right = new Node(data); break; } // If the right child is not null push it to the // queue else q.push(temp.right); } return root; } /* function to delete the given deepest node (d_node) in binary tree */ function deletDeepest(root, d_node) { const q = []; q.push(root); // Do level order traversal until last node let temp; while (q.length !== 0) { temp = q.shift(); if (temp === d_node) { temp = null; delete d_node; return; } if (temp.right) { if (temp.right === d_node) { temp.right = null; delete d_node; return; } else q.push(temp.right); } if (temp.left) { if (temp.left === d_node) { temp.left = null; delete d_node; return; } else q.push(temp.left); } } } /* function to delete element in binary tree */ function deletion(root, key) { if (!root) return null; if (root.left === null && root.right === null) { if (root.data === key) return null; else return root; } const q = []; q.push(root); let temp; let key_node = null; // Do level order traversal to find deepest // node(temp) and node to be deleted (key_node) while (q.length !== 0) { temp = q.shift(); if (temp.data === key) key_node = temp; if (temp.left) q.push(temp.left); if (temp.right) q.push(temp.right); } if (key_node !== null) { const x = temp.data; key_node.data = x; deletDeepest(root, temp); } return root; } // Inorder tree traversal (Left - Root - Right) function inorderTraversal(root) { if (!root) return; inorderTraversal(root.left); console.log(root.data + ' '); inorderTraversal(root.right); } // Preorder tree traversal (Root - Left - Right) function preorderTraversal(root) { if (!root) return; console.log(root.data + ' '); preorderTraversal(root.left); preorderTraversal(root.right); } // Postorder tree traversal (Left - Right - Root) function postorderTraversal(root) { if (root === null) return; postorderTraversal(root.left); postorderTraversal(root.right); console.log(root.data + ' '); } // Function for Level order tree traversal function levelorderTraversal(root) { if (root === null) return; // Queue for level order traversal const q = []; q.push(root); while (q.length !== 0) { const temp = q.shift(); console.log(temp.data + ' '); // Push left child in the queue if (temp.left) q.push(temp.left); // Push right child in the queue if (temp.right) q.push(temp.right); } } /* Driver function to check the above algorithm. */ let root = null; // Insertion of nodes root = insert(root, 10); root = insert(root, 20); root = insert(root, 30); root = insert(root, 40); console.log('Preorder traversal: '); preorderTraversal(root); console.log('
Inorder traversal: '); inorderTraversal(root); console.log('
Postorder traversal: '); postorderTraversal(root); console.log('
Level order traversal: '); levelorderTraversal(root); // Delete the node with data = 20 root = deletion(root, 20); console.log('
Inorder traversal after deletion: '); inorderTraversal(root); C++14 #include using namespace std; // Node class to define the structure of the node class Node { public: int data; Node *left, *right; // Parameterized Constructor Node(int val) { data = val; left = right = NULL; } }; // Function to insert nodes Node* insert(Node* root, int data) { // If tree is empty, new node becomes the root if (root == NULL) { root = new Node(data); return root; } // queue to traverse the tree and find the position to // insert the node queue q; q.push(raíz); while (!q.empty()) { Nodo* temp = q.front(); q.pop(); // Insertar nodo como hijo izquierdo del nodo padre if (temp->left == NULL) { temp->left = new Node(data); romper; } // Si el hijo izquierdo no es nulo, envíelo a la // cola else q.push(temp->left); // Insertar nodo como hijo derecho del nodo padre if (temp->right == NULL) { temp->right = new Node(data); romper; } // Si el hijo correcto no es nulo, envíelo a la // cola else q.push(temp->right); } devolver raíz; } /* función para eliminar el nodo más profundo dado (d_node) en el árbol binario */ void deletDeepest(Node* root, Node* d_node) { cola q; q.push(raíz); // Realiza un recorrido de orden de nivel hasta el último nodo Node* temp; mientras (!q.empty()) { temp = q.front(); q.pop(); if (temp == d_node) { temp = NULL; eliminar (d_nodo); devolver; } if (temp->derecha) { if (temp->derecha == d_node) { temp->derecha = NULL; eliminar (d_nodo); devolver; } else q.push(temp->right); } if (temp->izquierda) { if (temp->izquierda == d_node) { temp->izquierda = NULL; eliminar (d_nodo); devolver; } else q.push(temp->izquierda); } } } /* función para eliminar elemento en el árbol binario */ Eliminación de nodo*(Nodo* raíz, clave int) { if (!root) return NULL; if (raíz->izquierda == NULL && raíz->derecha == NULL) { if (raíz->datos == clave) return NULL; de lo contrario devolverá la raíz; } cola q; q.push(raíz); Temperatura del nodo*; Nodo* key_node = NULL; // Realice un recorrido de orden de niveles para encontrar el // nodo (temp) más profundo y el nodo que se eliminará (key_node) while (!q.empty()) { temp = q.front(); q.pop(); if (temp->datos == clave) key_node = temp; if (temp->izquierda) q.push(temp->izquierda); if (temperatura->derecha) q.push(temperatura->derecha); } if (key_node! = NULL) { int x = temp->datos; key_node->datos = x; eliminarMás profundo(raíz, temporal); } devolver raíz; } // Recorrido del árbol en orden (Izquierda - Raíz - Derecha) void inorderTraversal(Nodo* raíz) { if (!root) return; inorderTraversal(raíz->izquierda); corte < < root->datos < < ' '; inorderTraversal(root->bien); } // Recorrido del árbol de pedidos anticipados (Raíz - Izquierda - Derecha) void preorderTraversal(Nodo* raíz) { if (!root) return; corte < < root->datos < < ' '; preorderTraversal(root->izquierda); preordenTraversal(raíz->derecha); } // recorrido del árbol de postorden (Izquierda - Derecha - Raíz) void postorderTraversal(Node* root) { if (root == NULL) return; postorderTraversal(raíz->izquierda); postorderTraversal(raíz->derecha); corte < < root->datos < < ' '; } // Function for Level order tree traversal void levelorderTraversal(Node* root) { if (root == NULL) return; // Queue for level order traversal queue q; q.push(raíz); while (!q.empty()) { Nodo* temp = q.front(); q.pop(); corte < < temp->datos < < ' '; // Push left child in the queue if (temp->izquierda) q.push(temp->izquierda); // Empuja al hijo derecho en la cola if (temp->right) q.push(temp->right); } } /* Función del controlador para comprobar el algoritmo anterior. */ int main() { Nodo* raíz = NULL; // Inserción de nodos root = insert(root, 10); raíz = insertar(raíz, 20); raíz = insertar(raíz, 30); raíz = insertar(raíz, 40); corte < < 'Preorder traversal: '; preorderTraversal(root); cout < < '
Inorder traversal: '; inorderTraversal(root); cout < < '
Postorder traversal: '; postorderTraversal(root); cout < < '
Level order traversal: '; levelorderTraversal(root); // Delete the node with data = 20 root = deletion(root, 20); cout < < '
Inorder traversal after deletion: '; inorderTraversal(root); } Producción
Preorder traversal: 10 20 40 30 Inorder traversal: 40 20 10 30 Postorder traversal: 40 20 30 10 Level order traversal: 10 20 30 40 Inorder traversal after deletion: 40 10 30
Análisis de complejidad de las operaciones del árbol binario
| Operaciones | Complejidad del tiempo | Complejidad espacial |
|---|---|---|
| Inserción | EN) | EN) |
| Recorrido de pedidos anticipados | EN) | EN) |
| Recorrido en orden | EN) | EN) |
| Recorrido de giro postal | EN) | EN) |
| Recorrido de orden de nivel | EN) | EN) |
| Supresión | EN) | EN) |
| buscando | EN) | EN) |
Nosotros podemos usar Recorrido de Morris para atravesar todos los nodos del árbol binario en tiempo O (1).
Ventajas del árbol binario
- Búsqueda eficiente: Los árboles binarios son eficientes cuando se busca un elemento específico, ya que cada nodo tiene como máximo dos nodos secundarios, lo que permite Memoria eficiente: Los árboles binarios requieren menos memoria en comparación con otras estructuras de datos de árboles, por lo que son eficientes en memoria.
- Los árboles binarios son relativamente fáciles de implementar y comprender ya que cada nodo tiene como máximo dos hijos, el hijo izquierdo y el hijo derecho.
Desventajas del árbol binario
- Estructura limitada: Los árboles binarios están limitados a dos nodos secundarios por nodo, lo que puede limitar su utilidad en determinadas aplicaciones. Por ejemplo, si un árbol requiere más de dos nodos secundarios por nodo, una estructura de árbol diferente puede ser más adecuada.
- Árboles desequilibrados: Los árboles binarios desequilibrados, donde un subárbol es significativamente más grande que el otro, pueden generar operaciones de búsqueda ineficientes. Esto puede ocurrir si el árbol no está correctamente equilibrado o si los datos se insertan en un orden no aleatorio.
- Ineficiencia espacial: Los árboles binarios pueden ser ineficientes en términos de espacio en comparación con otras estructuras de datos. Esto se debe a que cada nodo requiere dos punteros secundarios, lo que puede representar una cantidad significativa de sobrecarga de memoria para árboles grandes.
- Rendimiento lento en los peores escenarios: En el peor de los casos, un árbol binario puede degenerarse o torcerse, lo que significa que cada nodo tiene un solo hijo. En este caso, las operaciones de búsqueda pueden degradarse a una complejidad temporal O(n), donde n es el número de nodos en el árbol.
Aplicaciones del árbol binario
- El árbol binario se puede utilizar para representar datos jerárquicos .
- Los árboles de codificación de Huffman se utilizan en algoritmos de compresión de datos .
- Cola de prioridad es otra aplicación del árbol binario que se utiliza para buscar máxima o mínima en complejidad temporal O(1).
- Útil para indexar segmentado en la base de datos y útil para almacenar caché en el sistema.
- Los árboles binarios se pueden utilizar para implementar árboles de decisión, un tipo de algoritmo de aprendizaje automático utilizado para clasificación y análisis de regresión.
Preguntas frecuentes sobre el árbol binario
1. ¿Qué es un árbol binario?
Un árbol binario es una estructura de datos no lineal que consta de nodos, donde cada nodo tiene como máximo dos hijos (hijo izquierdo y hijo derecho).
2. ¿Cuáles son los tipos de árboles binarios?
Los árboles binarios se pueden clasificar en varios tipos, incluidos árboles binarios completos, árboles binarios completos, árboles binarios perfectos, árboles binarios equilibrados (como los árboles AVL y los árboles Rojo-Negro) y árboles binarios degenerados (o patológicos).
3. ¿Cuál es la altura de un árbol binario?
La altura de un árbol binario es la longitud del camino más largo desde el nodo raíz hasta un nodo hoja. Representa el número de aristas en el camino más largo desde el nodo raíz hasta un nodo hoja.
4. ¿Cuál es la profundidad de un nodo en un árbol binario?
La profundidad de un nodo en un árbol binario es la longitud del camino desde el nodo raíz hasta ese nodo en particular. La profundidad del nodo raíz es 0.
5. ¿Cómo se realiza el recorrido de un árbol en un árbol binario?
El recorrido de un árbol binario se puede realizar de diferentes maneras: recorrido en orden, recorrido en orden previo, recorrido en orden posterior y recorrido en orden de nivel (también conocido como recorrido en amplitud).
6. ¿Qué es un recorrido en orden en un árbol binario?
En el recorrido en orden, los nodos se visitan recursivamente en este orden: hijo izquierdo, raíz, hijo derecho. Este recorrido da como resultado que los nodos se visiten en orden no decreciente en un árbol de búsqueda binario.
7. ¿Qué es un recorrido de pedido anticipado en el árbol binario?
En el recorrido de preorden, los nodos se visitan recursivamente en este orden: raíz, hijo izquierdo, hijo derecho. Este recorrido da como resultado que el nodo raíz sea el primer nodo en ser visitado.
8. ¿Qué es un recorrido de posorden en el árbol binario?
En el recorrido en posorden, los nodos se visitan recursivamente en este orden: hijo izquierdo, hijo derecho y raíz. Este recorrido da como resultado que el nodo raíz sea el último nodo en ser visitado.
9. ¿Cuál es la diferencia entre un árbol binario y un árbol de búsqueda binaria?
Un árbol binario es una estructura de datos jerárquica donde cada nodo tiene como máximo dos hijos, mientras que un árbol de búsqueda binario es un tipo de árbol binario en el que el hijo izquierdo de un nodo contiene valores menores que el valor del nodo y el hijo derecho contiene valores. mayor que el valor del nodo.
10. ¿Qué es un árbol binario equilibrado?
Un árbol binario equilibrado es un árbol binario en el que la altura de los subárboles izquierdo y derecho de cada nodo difiere como máximo en uno. Los árboles equilibrados ayudan a mantener operaciones eficientes como la búsqueda, la inserción y la eliminación con complejidades temporales cercanas a O (log n).
Conclusión:
El árbol es una estructura de datos jerárquica. Los usos principales de los árboles incluyen mantener datos jerárquicos, proporcionar acceso moderado y operaciones de inserción/eliminación. Los árboles binarios son casos especiales de árboles en los que cada nodo tiene como máximo dos hijos.
Artículos relacionados:
- Propiedades del árbol binario
- Tipos de árbol binario