Dado un nodo de una BST, ¿cómo encontrar la clave superior más próximo?
En sucesor Orden en árbol binario de búsqueda
Echa un vistazo aquí: finde sucesor en un árbol binario de búsqueda
En árbol binario, Inorder sucesor de un nodo es el nodo siguiente en el finde recorrido del árbol binario. Finde sucesor es NULL para el último nodo de Inoorder recorrido. En búsqueda binaria Árbol, Inorder sucesor de un nodo de entrada también se puede definir como el nodo con la clave más pequeño mayor que la clave del nodo de entrada.
La forma más general, depende de si usted tiene un enlace de los padres en sus nodos o no.
Si almacena el enlace de los padres
Luego tienes que elegir:
- El niño más a la izquierda del hijo derecho, si el nodo actual tiene un hijo derecho. Si el niño tiene derecho a ningún niño izquierdo, el hijo derecho es su sucesor finde.
- Navegue hasta los nodos antepasado de los padres, y cuando encuentre un padre cuyo hijo izquierdo es el nodo que está actualmente en, el padre es el sucesor finde de su nodo original.
Si tiene hijo derecho, hacer este enfoque (caso 1 anterior):

Si usted no tiene un hijo derecho, hacer este enfoque (caso 2 anterior):

Si no almacena el enlace de los padres
Luego hay que realizar un análisis completo del árbol, hacer el seguimiento de los nodos, por lo general con una pila, para que tenga la información necesaria para hacer básicamente el mismo que el primer método que se basaba en el enlace de los padres.
fuente por usuario Lasse Vågsæther Karlsen
He aquí una aplicación sin necesidad de enlaces de padres o estructuras intermedias (como una pila). Esta función sucesor en orden es un poco diferente a lo que la mayoría podría estar buscando ya que opera en la tecla en contraposición al nodo. Además, se encontró un sucesor de una clave, incluso si no está presente en el árbol. No es demasiado difícil de cambiar si usted necesitó, sin embargo.
public class Node<T extends Comparable<T>> {
private T data;
private Node<T> left;
private Node<T> right;
public Node(T data, Node<T> left, Node<T> right) {
this.data = data;
this.left = left;
this.right = right;
}
/*
* Returns the left-most node of the current node. If there is no left child, the current node is the left-most.
*/
private Node<T> getLeftMost() {
Node<T> curr = this;
while(curr.left != null) curr = curr.left;
return curr;
}
/*
* Returns the right-most node of the current node. If there is no right child, the current node is the right-most.
*/
private Node<T> getRightMost() {
Node<T> curr = this;
while(curr.right != null) curr = curr.right;
return curr;
}
/**
* Returns the in-order successor of the specified key.
* @param key The key.
* @return
*/
public T getSuccessor(T key) {
Node<T> curr = this;
T successor = null;
while(curr != null) {
// If this.data < key, search to the right.
if(curr.data.compareTo(key) < 0 && curr.right != null) {
curr = curr.right;
}
// If this.data > key, search to the left.
else if(curr.data.compareTo(key) > 0) {
// If the right-most on the left side has bigger than the key, search left.
if(curr.left != null && curr.left.getRightMost().data.compareTo(key) > 0) {
curr = curr.left;
}
// If there's no left, or the right-most on the left branch is smaller than the key, we're at the successor.
else {
successor = curr.data;
curr = null;
}
}
// this.data == key...
else {
// so get the right-most data.
if(curr.right != null) {
successor = curr.right.getLeftMost().data;
}
// there is no successor.
else {
successor = null;
}
curr = null;
}
}
return successor;
}
public static void main(String[] args) {
Node<Integer> one, three, five, seven, two, six, four;
one = new Node<Integer>(Integer.valueOf(1), null, null);
three = new Node<Integer>(Integer.valueOf(3), null, null);
five = new Node<Integer>(Integer.valueOf(5), null, null);
seven = new Node<Integer>(Integer.valueOf(7), null, null);
two = new Node<Integer>(Integer.valueOf(2), one, three);
six = new Node<Integer>(Integer.valueOf(6), five, seven);
four = new Node<Integer>(Integer.valueOf(4), two, six);
Node<Integer> head = four;
for(int i = 0; i <= 7; i++) {
System.out.println(head.getSuccessor(i));
}
}
}
Con árbol binario de búsqueda, el algoritmo para encontrar la más alta siguiente nodo de un nodo dado, básicamente, es encontrar el nodo más bajo del derecho de sub-árbol de ese nodo.
El algoritmo solo puede ser simplemente:
- Comenzar con el hijo derecho del nodo dado (que sea el nodo actual temporal)
- Si el nodo actual no tiene hijo izquierdo, que es el nodo más alta siguiente.
- Si el nodo actual tiene un hijo izquierdo, lo convierten en el nodo actual.
Repetir 2 y 3 hasta que encontremos más alta siguiente nodo.
Código Python a la Lasse respuesta :
def findNext(node):
if node.rightChild != None:
return findMostLeft(node.rightChild)
else:
parent = node.parent
while parent != None:
if parent.leftChild == node:
break
node = parent
parent = node.parent
return parent
C ++ solución suponiendo que los nodos tienen la izquierda, derecha, y Consejos para los Padres:
Esto ilustra la función Node* getNextNodeInOrder(Node)que devuelve la siguiente clave del árbol de búsqueda binario en orden.
#include <cstdlib>
#include <iostream>
using namespace std;
struct Node{
int data;
Node *parent;
Node *left, *right;
};
Node *createNode(int data){
Node *node = new Node();
node->data = data;
node->left = node->right = NULL;
return node;
}
Node* getFirstRightParent(Node *node){
if (node->parent == NULL)
return NULL;
while (node->parent != NULL && node->parent->left != node){
node = node->parent;
}
return node->parent;
}
Node* getLeftMostRightChild(Node *node){
node = node->right;
while (node->left != NULL){
node = node->left;
}
return node;
}
Node *getNextNodeInOrder(Node *node){
//if you pass in the last Node this will return NULL
if (node->right != NULL)
return getLeftMostRightChild(node);
else
return getFirstRightParent(node);
}
void inOrderPrint(Node *root)
{
if (root->left != NULL) inOrderPrint(root->left);
cout << root->data << " ";
if (root->right != NULL) inOrderPrint(root->right);
}
int main(int argc, char** argv) {
//Purpose of this program is to demonstrate the function getNextNodeInOrder
//of a binary tree in-order. Below the tree is listed with the order
//of the items in-order. 1 is the beginning, 11 is the end. If you
//pass in the node 4, getNextNode returns the node for 5, the next in the
//sequence.
//test tree:
//
// 4
// / \
// 2 11
// / \ /
// 1 3 10
// /
// 5
// \
// 6
// \
// 8
// / \
// 7 9
Node *root = createNode(4);
root->parent = NULL;
root->left = createNode(2);
root->left->parent = root;
root->right = createNode(11);
root->right->parent = root;
root->left->left = createNode(1);
root->left->left->parent = root->left;
root->right->left = createNode(10);
root->right->left->parent = root->right;
root->left->right = createNode(3);
root->left->right->parent = root->left;
root->right->left->left = createNode(5);
root->right->left->left->parent = root->right->left;
root->right->left->left->right = createNode(6);
root->right->left->left->right->parent = root->right->left->left;
root->right->left->left->right->right = createNode(8);
root->right->left->left->right->right->parent =
root->right->left->left->right;
root->right->left->left->right->right->left = createNode(7);
root->right->left->left->right->right->left->parent =
root->right->left->left->right->right;
root->right->left->left->right->right->right = createNode(9);
root->right->left->left->right->right->right->parent =
root->right->left->left->right->right;
inOrderPrint(root);
//UNIT TESTING FOLLOWS
cout << endl << "unit tests: " << endl;
if (getNextNodeInOrder(root)->data != 5)
cout << "failed01" << endl;
else
cout << "passed01" << endl;
if (getNextNodeInOrder(root->right) != NULL)
cout << "failed02" << endl;
else
cout << "passed02" << endl;
if (getNextNodeInOrder(root->right->left)->data != 11)
cout << "failed03" << endl;
else
cout << "passed03" << endl;
if (getNextNodeInOrder(root->left)->data != 3)
cout << "failed04" << endl;
else
cout << "passed04" << endl;
if (getNextNodeInOrder(root->left->left)->data != 2)
cout << "failed05" << endl;
else
cout << "passed05" << endl;
if (getNextNodeInOrder(root->left->right)->data != 4)
cout << "failed06" << endl;
else
cout << "passed06" << endl;
if (getNextNodeInOrder(root->right->left->left)->data != 6)
cout << "failed07" << endl;
else
cout << "passed07" << endl;
if (getNextNodeInOrder(root->right->left->left->right)->data != 7)
cout << "failed08 it came up with: " <<
getNextNodeInOrder(root->right->left->left->right)->data << endl;
else
cout << "passed08" << endl;
if (getNextNodeInOrder(root->right->left->left->right->right)->data != 9)
cout << "failed09 it came up with: "
<< getNextNodeInOrder(root->right->left->left->right->right)->data
<< endl;
else
cout << "passed09" << endl;
return 0;
}
¿Qué impresiones:
1 2 3 4 5 6 7 8 9 10 11
unit tests:
passed01
passed02
passed03
passed04
passed05
passed06
passed07
passed08
passed09
Usted puede leer información adicional aquí (Rus pulmón)
Node next(Node x)
if x.right != null
return minimum(x.right)
y = x.parent
while y != null and x == y.right
x = y
y = y.parent
return y
Node prev(Node x)
if x.left != null
return maximum(x.left)
y = x.parent
while y != null and x == y.left
x = y
y = y.parent
return y
todas estas respuestas parecen demasiado complicadas para mí. Realmente no necesitamos punteros de padres o cualquier estructura de datos auxiliares como una pila. Todo lo que tenemos que hacer es recorrer el árbol desde la raíz en orden, establecer un indicador en cuanto nos encontramos con el nodo de destino, y el siguiente nodo en el árbol que nos visita será el nodo en el orden sucesor. Aquí es una rutina rápida y sucia que escribí.
Node* FindNextInorderSuccessor(Node* root, int target, bool& done)
{
if (!root)
return NULL;
// go left
Node* result = FindNextInorderSuccessor(root->left, target, done);
if (result)
return result;
// visit
if (done)
{
// flag is set, this must be our in-order successor node
return root;
}
else
{
if (root->value == target)
{
// found target node, set flag so that we stop at next node
done = true;
}
}
// go right
return FindNextInorderSuccessor(root->right, target, done);
}
Si realizamos un recorrido en orden entonces se visita el subárbol izquierdo, entonces el nodo raíz y, finalmente, el subárbol derecho para cada nodo en el árbol. La realización de una en transversal fin nos dará las claves de un árbol binario de búsqueda en orden ascendente, por lo que cuando nos referimos a recuperar el fin sucesor de un nodo que pertenece a un árbol de búsqueda binaria nos referimos a lo que sería el siguiente nodo en la secuencia de el nodo dado.
Digamos que tenemos un nodo R y queremos que su sucesor en el orden que tendrían los siguientes casos.
[1] La raíz R tiene un nodo de la derecha, así que todo lo que tenemos que hacer es atravesar el nodo más a la izquierda del R-> derecha.
[2] La raíz R tiene ningún nodo de la derecha, en este caso se recorre una copia de seguridad del árbol siguiendo los enlaces de los padres hasta que el nodo R es un hijo izquierdo de su padre, cuando esto ocurre tenemos el nodo padre P como el de sucesor fin .
[3] Estamos en el nodo de extrema derecha del árbol, en este caso no es ningún sucesor en orden.
La aplicación se basa en la siguiente definición de nodo
class node
{
private:
node* left;
node* right;
node* parent
int data;
public:
//public interface not shown, these are just setters and getters
.......
};
//go up the tree until we have our root node a left child of its parent
node* getParent(node* root)
{
if(root->parent == NULL)
return NULL;
if(root->parent->left == root)
return root->parent;
else
return getParent(root->parent);
}
node* getLeftMostNode(node* root)
{
if(root == NULL)
return NULL;
node* left = getLeftMostNode(root->left);
if(left)
return left;
return root;
}
//return the in order successor if there is one.
//parameters - root, the node whose in order successor we are 'searching' for
node* getInOrderSucc(node* root)
{
//no tree, therefore no successor
if(root == NULL)
return NULL;
//if we have a right tree, get its left most node
if(root->right)
return getLeftMostNode(root->right);
else
//bubble up so the root node becomes the left child of its
//parent, the parent will be the inorder successor.
return getParent(root);
}
solución de JavaScript - Si el nodo dado tiene un nodo de la derecha, y luego volver al nodo más pequeño en el subárbol derecho - Si no, entonces hay 2 posibilidades: - El nodo dado es un hijo izquierdo del nodo padre. Si es así, devolver el nodo padre. De lo contrario, el nodo dado es un hijo derecho del nodo padre. Si es así, devolver el hijo derecho del nodo padre
function nextNode(node) {
var nextLargest = null;
if (node.right != null) {
// Return the smallest item in the right subtree
nextLargest = node.right;
while (nextLargest.left !== null) {
nextLargest = nextLargest.left;
}
return nextLargest;
} else {
// Node is the left child of the parent
if (node === node.parent.left) return node.parent;
// Node is the right child of the parent
nextLargest = node.parent;
while (nextLargest.parent !== null && nextLargest !== nextLargest.parent.left) {
nextLargest = nextLargest.parent
}
return nextLargest.parent;
}
}
Hacer esto en Java
TreeNode getSuccessor(TreeNode treeNode) {
if (treeNode.right != null) {
return getLeftMostChild(treeNode.right);
} else {
TreeNode p = treeNode.parent;
while (p != null && treeNode == p.right) { // traverse upwards until there is no parent (at the last node of BST and when current treeNode is still the parent's right child
treeNode = p;
p = p.parent; // traverse upwards
}
return p; // returns the parent node
}
}
TreeNode getLeftMostChild(TreeNode treeNode) {
if (treeNode.left == null) {
return treeNode;
} else {
return getLeftMostChild(treeNode.left);
}
}
Podemos dividir esta en 3 casos:
Si el nodo es un padre: En este caso nos encontramos si tiene un nodo de la derecha y atravesar al niño más a la izquierda del nodo derecha. En caso de que el nodo de la derecha no tiene hijos, entonces el nodo de la derecha es su sucesor finde. Si no hay ningún nodo derecho tenemos que mover el árbol para encontrar el sucesor finde.
Si el nodo es un hijo izquierdo: En este caso el padre es el sucesor finde.
Si el nodo (lo llaman x) es un hijo derecho (de su padre inmediato): Atravesamos el árbol hasta que nos encontramos con un nodo cuyo subárbol izquierdo tiene x.
Caso extremo: Si el nodo es el nodo de esquina más a la derecha, no hay sucesor finde.
Cada "tutorial" que he comprobado en google y todas las respuestas en este hilo utiliza la siguiente lógica: " Si el nodo no tiene un niño en ese momento su sucesor en la orden será uno de sus antepasados Uso de Enlace de los padres seguir viajando hasta. se obtiene el nodo que es el hijo izquierdo de su padre. Entonces este nodo padre será el sucesor en orden. "
Esto es lo mismo que pensar " si mi padre es más grande que yo, entonces yo soy el hijo izquierdo " (propiedad de un árbol de búsqueda binaria). Esto significa que sólo tiene que subir por la cadena principal hasta que la propiedad es verdadera. En la que los resultados de mi opinión en un código más elegante.
Creo que la razón por la cual todo el mundo está comprobando " soy el hijo izquierdo " examinado ramas en lugar de valores en la ruta de código que utiliza enlaces de padres proviene de la lógica "préstamo" del algoritmo-no-enlace-a-padre.
También desde el código incluido a continuación podemos ver hay ninguna necesidad de estructura de datos de la pila como se sugiere en otras respuestas.
Lo que sigue es una simple función de C ++ que funciona tanto para los casos de uso (con y sin utilizar el enlace a los padres).
Node* nextInOrder(const Node *node, bool useParentLink) const
{
if (!node)
return nullptr;
// when has a right sub-tree
if (node->right) {
// get left-most node from the right sub-tree
node = node->right;
while (node->left)
node = node->left;
return node;
}
// when does not have a right sub-tree
if (useParentLink) {
Node *parent = node->parent;
while (parent) {
if (parent->value > node->value)
return parent;
parent = parent->parent;
}
return nullptr;
} else {
Node *nextInOrder = nullptr;
// 'root' is a class member pointing to the root of the tree
Node *current = root;
while (current != node) {
if (node->value < current->value) {
nextInOrder = current;
current = current->left;
} else {
current = current->right;
}
}
return nextInOrder;
}
}
Node* previousInOrder(const Node *node, bool useParentLink) const
{
if (!node)
return nullptr;
// when has a left sub-tree
if (node->left) {
// get right-most node from the left sub-tree
node = node->left;
while (node->right)
node = node->right;
return node;
}
// when does not have a left sub-tree
if (useParentLink) {
Node *parent = node->parent;
while (parent) {
if (parent->value < node->value)
return parent;
parent = parent->parent;
}
return nullptr;
} else {
Node *prevInOrder = nullptr;
// 'root' is a class member pointing to the root of the tree
Node *current = root;
while (current != node) {
if (node->value < current->value) {
current = current->left;
} else {
prevInOrder = current;
current = current->right;
}
}
return prevInOrder;
}
}
C # aplicación (no recursiva!) Para encontrar el nodo 'siguiente' de un nodo dado en un árbol de búsqueda binario en el que cada nodo tiene un enlace a su matriz.
public static Node WhoIsNextInOrder(Node root, Node node)
{
if (node.Right != null)
{
return GetLeftMost(node.Right);
}
else
{
Node p = new Node(null,null,-1);
Node Next = new Node(null, null, -1);
bool found = false;
p = FindParent(root, node);
while (found == false)
{
if (p.Left == node) { Next = p; return Next; }
node = p;
p = FindParent(root, node);
}
return Next;
}
}
public static Node FindParent(Node root, Node node)
{
if (root == null || node == null)
{
return null;
}
else if ( (root.Right != null && root.Right.Value == node.Value) || (root.Left != null && root.Left.Value == node.Value))
{
return root;
}
else
{
Node found = FindParent(root.Right, node);
if (found == null)
{
found = FindParent(root.Left, node);
}
return found;
}
}
public static Node GetLeftMost (Node node)
{
if (node.Left == null)
{
return node;
}
return GetLeftMost(node.Left);
}
Podemos encontrar el sucesor en O (log n) sin el uso de punteros de padres (para un árbol equilibrado).
La idea es muy similar a cuando se tiene punteros padres.
Podemos definir una función recursiva que logra esto de la siguiente manera:
- Si el nodo actual es el objetivo, devolver el nodo más a la izquierda / más pequeño de su subárbol derecho, si es que existe.
- Recurse izquierda si el objetivo es más pequeño que el nodo actual, ya la derecha si es mayor.
- Si el destino está a la izquierda y no hemos encontrado un sucesor sin embargo, devuelva el nodo actual.
Pseudo-código:
Key successor(Node current, Key target):
if current == null
return null
if target == current.key
if current.right != null
return leftMost(current.right).key
else
return specialKey
else
if target < current.key
s = successor(current.left, target)
if s == specialKey
return current.key
else
return s
else
return successor(current.right, target)
Node leftMost(Node current):
while current.left != null
current = current.left
return current
No necesitamos enlace padre o pila para encontrar el sucesor en el orden en O (log n) (suponiendo árbol equilibrado). Mantener una variable temporal con el valor más reciente encontrado en el recorrido en orden que es mayor que la tecla. Si recorrido en orden determina que el nodo no tiene un hijo derecho, entonces esto sería el sucesor finde. lo demás, el descendiente más a la izquierda del hijo derecho.













