Árbol roji-negro gráfico en Java.
Otro de mis proyectos de la época universitaria, otro árbol roji-negro, en una entrada de hace tiempo
publique un árbol roji-negro en c ahora me he encontrado este en java y modo gráfico.

Tal como se puede apreciar en la imagen este programa implementa las acciones de insertar y buscar un nodo, además de
mostrar el arbol en modo gráfico. El programa consta de dos clases principales y algunas clases internas, a continuación
el código fuente, el cual tiene varios comentarios, donde se habla de casos se refiere a los mostrados en la entrada árbol roji-negro en c
public class Nodo{
public int numero; // el numero del nodo
public boolean rojo; // indica true es rojo; false es negro
public Nodo izquierdo; // apuntador al nodo hijo izquierdo
public Nodo derecho; // apuntador al nodo hijo derecho
public Nodo padre; // apuntador al nodo padre
public String texto; // texto del nodo
// contruye un nuevo nodo asignadole numero, texto y su color
public Nodo(int numero, String texto, boolean rojo){
this.numero=numero;
this.texto=texto;
this.rojo=rojo;
izquierdo=null;
derecho=null;
padre=null;
}
}
// version del compilador java 1.6.0_13
import java.awt.*;
import java.awt.event.*;
public class Window extends Frame implements ActionListener{
private TextField textoNumero; // campo para ingresar el numero del nuevo nodo que se desea insertar
private TextField textoString; // campo para ingresar el texto del nuevo nodo que se desea insertar
private Button botonInserta; // boton para la accion de insertar un nuevo nodo
private TextField texto2Numero; // campo para ingresar el numero del nodo que se desea buscar
private Button botonBusca; // boton para la accion de buscar un nodo
private TextField textoMensaje; // campo para mandar mensajes al usuario
private Nodo primerNodo; // nodo raiz del arbol
private ScrollPane scrollpane; // area para desplegar el arbol graficamente
private Label grafica; // control utilizado para graficar el arbol
private Nodo nodEncontrado; // para busquedas guarda la referencia del nodo encontrado para resaltarlo
// constructor de la clase crea la ventana y sus controles
public Window(){
// creando los controles y sus caracteristicas
Panel pan1=new Panel(new GridLayout(3,0));
Panel reg1=new Panel(new FlowLayout());
textoNumero = new TextField(5);
textoString = new TextField(35);
Button botonInserta = new Button("Insertar");
botonInserta.addActionListener(this);
Panel reg2=new Panel(new FlowLayout());
texto2Numero = new TextField(5);
botonBusca = new Button("Buscar");
botonBusca.addActionListener(this);
Panel reg3=new Panel(new FlowLayout());
textoMensaje = new TextField(65);
textoMensaje.setEditable(false);
grafica=new Label(){ // sobrecarga del metodo paint del control para hacer que dibuje el arbol
public void paint(Graphics grph){
dibujaArbol(grph);
};
};
scrollpane=new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
scrollpane.add(grafica);
pan1.add(reg1);
pan1.add(reg2);
pan1.add(reg3);
reg1.add(botonInserta);
reg1.add(new Label("Numero:"));
reg1.add(textoNumero);
reg1.add(new Label("String:"));
reg1.add(textoString);
reg2.add(botonBusca);
reg2.add(new Label("Numero:"));
reg2.add(texto2Numero);
reg3.add(new Label("Mensaje:"));
reg3.add(textoMensaje);
add(pan1,BorderLayout.NORTH);
add(scrollpane,BorderLayout.CENTER);
addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
primerNodo=null;
nodEncontrado=null;
// dandole caracteristicas a la ventana
setTitle("Arbol roji-negro");
setSize(666,578);
setVisible(true);
}
// realiza lass opciones de los botones
public void actionPerformed(ActionEvent e){
Button fuente=(Button)e.getSource();
int numero=0;
String textonum="";
textoMensaje.setText("");
nodEncontrado=null;
try{
if(fuente.getLabel().equals("Insertar")){
// inserta un nuevo nodo si el numero ingresado es un entero valido y la string no esta vacia
if(textoString.getText().length()==0){
// si la cadena string no fue ingresada enviar mensaje de error
textoMensaje.setText("String vacia");
return;
}
textonum=textoNumero.getText();
numero=Integer.parseInt(textonum);
insertaUnNodo(numero,textoString.getText());
textoMensaje.setText("Insertado "+numero);
}
else if(fuente.getLabel().equals("Buscar")){
// busca un nodo si el numero ingresado es un entero valido
textonum=texto2Numero.getText();
numero=Integer.parseInt(textonum);
nodEncontrado=buscaUnNodo(numero);
if(nodEncontrado==null) textoMensaje.setText("No se encontro");
else textoMensaje.setText("Encontrado: ["+nodEncontrado.numero+","+nodEncontrado.texto+"]");
}
}
catch(NumberFormatException ex){
// si el numero no es valido enviar mensaje de error
textoMensaje.setText("Error numero: ["+textonum+"]");
}
grafica.repaint();
}
// metodo que inserta el nodo en el arbol recibe el numero y texto del nuevo nodo
private void insertaUnNodo(int numero, String texto){
Nodo nuevoNodo;
boolean esHijoDer;
if(primerNodo==null) // ve si el arbol esta vacio y crea el nodo como raiz
primerNodo=new Nodo(numero,texto,false);
else{
nuevoNodo=new Nodo(numero,texto,true);
nuevoNodo.padre=primerNodo;
while(true){
// recorre los nodos que existan para buscar el lugar del nuevo nodo en base a su numero
if(numero<nuevoNodo.padre.numero){
if(nuevoNodo.padre.izquierdo!=null) nuevoNodo.padre=nuevoNodo.padre.izquierdo;
else{
nuevoNodo.padre.izquierdo=nuevoNodo;
esHijoDer=false;
break;
}
}
else{
if(nuevoNodo.padre.derecho!=null) nuevoNodo.padre=nuevoNodo.padre.derecho;
else{
nuevoNodo.padre.derecho=nuevoNodo;
esHijoDer=true;
break;
}
}
};
// en caso de que se presente que el padre del nuevo nodo es rojo envia al metodo que soluciona esto
if(nuevoNodo.padre.rojo) casoRojoRojo(nuevoNodo.padre,esHijoDer);
}
// actualizar la grafica del arbol
grafica.repaint();
}
// soluciona si al insertar se presenta un caso donde el nodo padre del nuevo nodo es rojo
private void casoRojoRojo(Nodo n, boolean hijoDer){
Nodo padreDePadre=n.padre;
Nodo hermanoDePadre;
Nodo temporal;
if(padreDePadre.izquierdo!=null && padreDePadre.derecho!=null){
// caso uno y dos: volver a colorear
if(n==padreDePadre.izquierdo) hermanoDePadre=padreDePadre.derecho;
else hermanoDePadre=padreDePadre.izquierdo;
if(hermanoDePadre.rojo){
hermanoDePadre.rojo=false; n.rojo=false;
if(padreDePadre!=primerNodo) padreDePadre.rojo=true;
if(padreDePadre.padre!=null){
if(padreDePadre.padre.rojo) // revisar que no se haya creado un caso rojo-rojo hacia arriba
casoRojoRojo(padreDePadre.padre,padreDePadre.padre.izquierdo!=padreDePadre);
}
return;
}
}
if(!hijoDer && padreDePadre.izquierdo==n){
// caso tres: reestructurar
n.rojo=false; padreDePadre.rojo=true;
temporal=n.derecho; n.derecho=padreDePadre; n.padre=padreDePadre.padre;
padreDePadre.padre=n; padreDePadre.izquierdo=temporal;
if(temporal!=null) temporal.padre=padreDePadre;
if(n.padre!=null){
temporal=n.padre;
if(temporal.izquierdo==n.derecho) temporal.izquierdo=n;
else temporal.derecho=n;
}
else primerNodo=n;
}
else if(hijoDer && padreDePadre.derecho==n){
// caso cuatro: reestructurar
n.rojo=false; padreDePadre.rojo=true;
temporal=n.izquierdo; n.izquierdo=padreDePadre; n.padre=padreDePadre.padre;
padreDePadre.padre=n; padreDePadre.derecho=temporal;
if(temporal!=null) temporal.padre=padreDePadre;
if(n.padre!=null){
temporal=n.padre;
if(temporal.izquierdo==n.izquierdo) temporal.izquierdo=n;
else temporal.derecho=n;
}
else primerNodo=n;
}
else if(hijoDer && padreDePadre.izquierdo==n){
// caso cinco: reestructurar
hermanoDePadre=n.derecho; temporal=hermanoDePadre.izquierdo; padreDePadre.izquierdo=hermanoDePadre;
hermanoDePadre.padre=padreDePadre; hermanoDePadre.izquierdo=n; n.padre=hermanoDePadre;
n.derecho=temporal;
if(temporal!=null) temporal.padre=n;
// lleva al caso tres
casoRojoRojo(hermanoDePadre,false);
}
else if(!hijoDer && padreDePadre.derecho==n){
// caso seis: reestructurar
hermanoDePadre=n.izquierdo; temporal=hermanoDePadre.derecho; padreDePadre.derecho=hermanoDePadre;
hermanoDePadre.padre=padreDePadre; hermanoDePadre.derecho=n; n.padre=hermanoDePadre;
n.izquierdo=temporal;
if(temporal!=null) temporal.padre=n;
// lleva al caso cuatro
casoRojoRojo(hermanoDePadre,true);
}
}
// metodo para buscar un nodo si no lo encuentra regresa null
private Nodo buscaUnNodo(int numero){
Nodo temporal=primerNodo;
if(temporal==null) return null;
do{
if(numero==temporal.numero) return temporal;
else if(numero<temporal.numero) temporal=temporal.izquierdo;
else if(numero>temporal.numero) temporal=temporal.derecho;
}while(temporal!=null);
return null;
}
// funcion que prepara el control grafica para dibujar el arbol
private void dibujaArbol(Graphics grph){
// tamaño del nodo 50x50 espacio entre nodos vertical 20 horizontal minimo 20
// calcular tamaño de toda la grafica
int altura=calculaProfundidad(primerNodo);
int anchura=(int)Math.pow(2,(altura-1));
if(altura==0) return;
grafica.setPreferredSize(new Dimension((anchura*70)+40,(70*altura)+20));
grph.setColor(Color.WHITE);
grph.clearRect(0,0,(anchura*70)+40,(70*altura)+20);
// enviar a dibujar el nodo raiz y este a su vez enviara al resto de los nodos
dibujaNodo(grph,primerNodo,25,(int)((70*anchura))/2);
scrollpane.doLayout();
}
// calcula la el numero de nodos de profundidad del arbol para calcular el arrea que ocupara el grafico
// segun el numero de nodos
private int calculaProfundidad(Nodo inicial){
int profIzq=0;
int profDer=0;
if(inicial==null) return 0;
if(inicial.izquierdo!=null)
profIzq=calculaProfundidad(inicial.izquierdo);
if(inicial.derecho!=null)
profDer=calculaProfundidad(inicial.derecho);
return (profIzq>profDer?profIzq:profDer)+1;
}
// dibuja un nodo y si este nodo tiene al menos un hijo tambien lo envia a dibujar
private void dibujaNodo(Graphics grph, Nodo nodo, int y, int x){
Color color=(nodo!=null?(nodo.rojo?Color.RED:Color.BLACK):Color.YELLOW);
int altura=calculaProfundidad(nodo)-1;
int anchura=(int)Math.pow(2,altura);
anchura=((anchura*70)+40)/4;
if(nodo!=null && (nodo.izquierdo!=null||nodo.derecho!=null)){
grph.setColor(Color.GREEN);
grph.drawLine(x+25,y+25,x-anchura+25,y+95);
grph.drawLine(x+25,y+25,x+anchura+25,y+95);
}
grph.setColor(color);
if(nodo!=null && nodo.rojo) grph.fillRect(x,y,50,50);
grph.fillOval(x,y,50,50);
if(nodo!=null){
if(nodo==nodEncontrado){
grph.setColor(Color.GREEN);
for(int c=2;c<=3;c++)
if(!nodEncontrado.rojo) grph.drawOval(x+c,y+c,48-(2*c),48-(2*c));
else grph.drawRect(x+c,y+c,49-(2*c),49-(2*c));
}
grph.setColor(nodo.rojo?Color.BLACK:Color.WHITE);
grph.drawString(""+nodo.numero,x+20,y+30);
if(nodo.izquierdo!=null||nodo.derecho!=null){
dibujaNodo(grph,nodo.izquierdo,y+70,(x-anchura));
dibujaNodo(grph,nodo.derecho,y+70,(x+anchura));
}
}
else{
grph.setColor(Color.BLACK);
grph.drawString("null",x+15,y+30);
}
}
public static void main(String[] args){
Window win = new Window();
}
}


Hola, muy buen trabajo, como se podría modificar dicho programa para dar la opción también de eliminar un elemento?
ResponderBorrarHabria que implementar la reestructuracion al momento de eliminar, aca http://es.wikipedia.org/wiki/%C3%81rbol_rojo-negro#Eliminaci.C3.B3n hay una explicacion y algo de codigo de ejemplo (en c), solo seria seguires logica:
Borrar