Programación en Java/Apéndices/Implementación del Algoritmo de Kruskal en Java

El algoritmo de Kruskal es un algoritmo de la teoría de grafos que busca encontrar un árbol recubridor mínimo de un grafo dado. Aquí encontraremos una implementación en Java incluyendo una interfaz gráfica.

Descripción del Problema

editar

El algoritmo de kruskal, es un algoritmo voraz utilizado en la teoría de grafos, con el fin de encontrar un árbol recubridor mínimo de un grafo conexo y ponderado.

El algoritmo de kruskal consiste en:

  • Paso 0: Iniciar el árbol T con n nodos y sin arcos
            T=({1, 2, …n},ø)
  • Paso 1: Con los arcos de G crear una lista L de arcos, en orden ascendente de peso. Los arcos con el mismo peso son ordenados arbitrariamente.
  • Paso 2. Seleccionar el arco (i,j) que esté al comienzo de L. Si éste forma un circuito en T no se transfiere a T y se borra de L y se repite el paso 2. Si no forma circuito en T se transfiere a T y se borra de L.
  • Paso 3. Si L es no vacío, volver al paso 2, de lo contrario PARAR.

Limitaciones

editar

Las únicas limitaciones que se presentan con el problema de la implementación del algoritmo de Kruskal es la creación de un algoritmo adicional que nos compruebe que al adicionar una arista al grafo no nos haga un ciclo.

El algoritmo implementado es el siguiente:

 public boolean HayCiclo(Grafo g,Arco aVerificar,Nodo terminal,String N)
 {
 	ArrayList<Enlace> aux=terminal.getEnlaces();
 
 	if(aux.size()==0)
 		return false;
 
 	if(terminal.existeEnlace(aVerificar.getInicial())!=-1)
 		return true;
 
 	for(int i=0;i<aux.size();i++)
 	{
 		Enlace nodo=aux.get(i);
 
 		if(nodo.getDestino().equals(N)==false)
 			if( HayCiclo(g,aVerificar,g.getNodo(nodo.getDestino()),terminal.getNombre()))
 								return true;
 	}
 
 	return false;
 }

Solución

editar

En la implementación del algoritmo de Kruskal en Java, se hizo uso de una estructura de datos adicional llamada grafo y adicionalmente se implementó una interfaz gráfica.

Estructura Grafo

editar

Clase Enlace

editar

// creamos un clase llamada enlace

La clase enlace se utiliza para hacer refencia al nodo terminal y al peso de un arco.

 public class Enlace 
 {
 	private String destino;
 	private double peso;
 
 	public Enlace(String desti, double peso1)
 	{
 		destino = desti;
 		peso = peso1;
 	}
 
 	public Enlace(String desti)
 	{
 		destino = desti;
 		peso = -1;
 	}
 
 	public void modificar(double peso1)
 	{
 		peso = peso1;
 	}
 
 	public String getDestino()
 	{
 		return destino;
 	}
 
 	public double getPeso()
 	{
 		return peso;
 	}
 }

Clase Nodo

editar

Hace referencia a un nodo del grafo, guardando un nombre y una serie de enlaces a otros nodos.

 import java.util.ArrayList;
 import javax.swing.JOptionPane;
 
 public class Nodo
 {
 	private String nombre;
 	private ArrayList<Enlace> enlaces;
 	private int enlacesExistentes;
 
 	public ArrayList<Enlace> getEnlaces()
 	{
 		return enlaces;
 	}
 
 	public Nodo(String newName)
 	{
 		nombre = newName;
 		enlacesExistentes = -1;
 		enlaces = new ArrayList<Enlace>();
 	}
 
 	public int getEnlacesExistentes()
 	{
 		return enlacesExistentes;
 	}
 
 	public String getNombre()
 	{
 		return nombre;
 	}
 
 	public void agregarEnlace(String enlazar,double peso)
 	{
 		if (enlacesExistentes == -1)
 		{
 			enlaces.add(new Enlace(enlazar,peso));
 			enlacesExistentes++;
 		}
 		else
 		{
 			int posicion;
 			posicion = existeEnlace(enlazar);
 			if (posicion == -1)
 			{
 				enlaces.add(new Enlace(enlazar,peso));
 				enlacesExistentes++;
 			}
 		}
 	}
 
 	public int existeEnlace(String enlazar)
 	{
 		for (int i = 0; i < enlaces.size(); i++)
 		{
 			Enlace miEnlace;
 			miEnlace = enlaces.get(i);
 			if (miEnlace.getDestino().equals(enlazar))
 				return i;
 		}
 		return -1;
 	}
 
 	public double enlacePosicion(int posi)
 	{
 		Enlace miEnlace;
 		miEnlace = enlaces.get(posi);
 		return miEnlace.getPeso();
 	}
 
 	public String NodoPosicion(int posi)
 	{
 		Enlace miEnlace;
 		miEnlace = enlaces.get(posi);
 		return miEnlace.getDestino();
 	}
 
 	boolean eliminarEnlace(int posicion)
 	{
 		if (posicion >= 0 && posicion <= enlaces.size())
 		{
 			enlaces.remove(posicion);
 			enlacesExistentes--;
 			return true;
 		}
 		else
 			JOptionPane.showMessageDialog(null, "No hay enlace en la posicion " + posicion); 
 	 	return false;
 	}
 }

Clase Arco

editar

Guarda la referencia del nodo inicial, nodo terminal y peso de un determinado arco del grafo.

 public class Arco
 {
 	private String inicial;
 	private String terminal;
 	private float peso;
 
 	public Arco(String ini, String ter, float pes)
 	{
 		inicial = ini;
 		terminal = ter;
 		peso = pes;
 	}
 
 	public float getPeso()
 	{
 		return peso;
 	}
 
 	public void setPeso(float peso)
 	{
 		this.peso = peso;
 	}
 
 	public String getInicial()
 	{
 		return inicial;
 	}
 
 	public void setInicial(String inicial)
 	{
 		this.inicial = inicial;
 	}
 
 	public String getTerminal()
 	{
 		return terminal;
 	}
 
 	public void setTerminal(String terminal)
 	{
 		this.terminal = terminal;
 	}
 
 	public String toString()
 	{
 		return "(" + inicial + ", " + terminal + ", " + peso + ")";
 	}
 }

Clase Grafo

editar

Representa la estructura grafo, compuesta por nodos y arcos.

 import java.util.Hashtable;
 import java.util.ArrayList;
 
 public class Grafo
 {
 	ArrayList <String>nombres;
 	ArrayList <Arco>aristas;
 	Hashtable <String,Nodo> nodos;
 
 	public Grafo()
 	{
 		nombres=new ArrayList<String>();
 		nodos=new Hashtable <String,Nodo>();
 		aristas=new ArrayList <Arco>();
 	}
 
 	public void ingresarNodo(String nombre)
 	{
 		nombres.add(nombre);
 		nodos.put(nombre,new Nodo(nombre));
 	}
 	public void adicionarEnlace(String nodoInicial,String nodoTerminal,float peso)
 	{
 		Arco nuevo=new Arco(nodoInicial,nodoTerminal,peso);
 		int i=buscarIndice(nuevo.getPeso());
 
 		if(i==-1)
 			aristas.add(nuevo);
 		else
 			aristas.add(i,nuevo);
 
 		nodos.get(nodoInicial).agregarEnlace(nodoTerminal,peso);
 		nodos.get(nodoTerminal).agregarEnlace(nodoInicial,peso);
 	}
 	public boolean busarArista(Arco arco)
 	{
 		for(int i=0;i<aristas.size();i++)
 		{
 			Arco otro=aristas.get(i);
 			if(arco.getInicial().equals(otro.getInicial())&&arco.getTerminal().equals(otro.getTerminal())&&arco.getPeso()==otro.getPeso())
 			{
 				aristas.remove(otro);
 				return true;
 			}
 		}
 		return false;
 	}
 	public int buscarIndice(float peso)
 	{
 		for(int i=0;i<aristas.size();i++)
 		{
  			if(peso<aristas.get(i).getPeso())
 				return i;
 		}
 		return -1;
 	}
 	public Hashtable getNodos()
 	{
 		return nodos;
 	}
 	public void setNodos(Hashtable<String,Nodo > muchos)
 	{
 		nodos=muchos;
 	}
 	public ArrayList<String> getNombres()
 	{
 		return nombres;
 	}
 	public Nodo getNodo(String nombre)
 	{
 		return (Nodo)nodos.get(nombre);
 	}
 
 	public ArrayList<Arco> getAristas() {
 		return aristas;
 	}
 
 	public void setAristas(ArrayList<Arco> aristas) {
 		this.aristas = aristas;
 	}
 
 	public void setNombres(ArrayList<String> nombres) {
 		this.nombres = nombres;
 	}
 
 }

Interfaz Gráfica

editar

Clase Lienzo

editar

Es el lienzo sobre el cual se dibujará el grafo.

 import java.awt.*;
 import java.util.ArrayList;
 
 import javax.swing.JComponent;
 
 public class Lienzo extends JComponent
 {
 	private ArrayList<Punto> puntos;
 	private ArrayList<Arista> aristas;
 	private ArrayList<Arista> neo;
 	private Point a, b;
 	public boolean estado = false;
 	public boolean punto = false;
 
 	public Lienzo()
 	{
 		aristas = new ArrayList<Arista>();
 		puntos = new ArrayList<Punto>();
 		neo = new ArrayList<Arista>();
 	}
 
 	public void paintComponent(Graphics g)
 	{
 		g.setColor(Color.BLUE);
 		g.drawLine((int) a.getX() + 5, (int) a.getY() + 5, (int) b.getX() + 5, (int) b.getY() + 5);
 		final Arista arista = (Arista) aristas.get(i);
 		arista.pintarRecta(g);
 		final Arista arista = (Arista) neo.get(i);
 		arista.setColor(Color.RED);
 		arista.pintarRecta(g);

 		for (int i = 0; i < puntos.size(); i++)
 		{
 			final Punto punto = (Punto) puntos.get(i);
 			punto.pintarPunto(g);
 		}
 	}
 
 	public void cambiarGrafo(Grafo nuevo)
 	{
 		Arco aux;
 
 		for (int i = 0; i < aristas.size(); i++)
 		{
 			aux = aristas.get(i).getArista();
 
 			if (nuevo.busarArista(aux) == true)
 				neo.add(aristas.get(i));
 		}
 
 		for (int i = 0; i < aristas.size(); i++)
 		{
 			final Arco n = aristas.get(i).getArista();
 			nuevo.getAristas().add(n);
 		}
 
 		estado = true;
 
 		repaint();
 	}
 
 	public ArrayList<Punto> getPuntos()
 	{
 		return puntos;
 	}
 
 	public void setPuntos(final ArrayList<Punto> puntos)
 	{
 		this.puntos = puntos;
 	}
 
 	public ArrayList<Arista> getAristas()
 	{
 		return aristas;
 	}
 
 	public void setAristas(final ArrayList<Arista> aristas)
 	{
 		this.aristas = aristas;
 	}
 
 	public ArrayList<Arista> getNeo()
 	{
 		return neo;
 	}
 
 	public void setNeo(final ArrayList<Arista> neo)
 	{
 		this.neo = neo;
 	}
 
 	public void setA(Point a)
 	{
 		this.a = a;
 	}
 
 	public void setB(Point b)
 	{
 		this.b = b;
 	}
 }

Clase Punto

editar

Permite dibujar un punto sobre el lienzo. Hace referencia a la posición del punto sobre el lienzo.

 import java.awt.Color;
 import java.awt.Graphics;
 import java.awt.Point; 
 
 public class Punto
 {
 	private Point ubicacion;
 	private String nombre;
 	private Color colorPunto = Color.BLUE;
 	private static final int RADIO = 10;
 
 	public Punto(int x, int y, String nombre)
 	{
 		ubicacion = new Point(x, y);
 		this.nombre = nombre;
 	}
 
 	public void pintarPunto(Graphics g)
 	{
 		g.setColor(Color.BLACK);
 		g.drawString(nombre, ubicacion.x - 3, ubicacion.y - 3);
 		g.setColor(colorPunto);
 		g.fillOval(ubicacion.x, ubicacion.y, 10, 10);
 	}
 
 	public void pintarPunto(Graphics g, Color color)
 	{
 		g.setColor(Color.BLACK);
 		g.drawString(nombre, ubicacion.x - 3, ubicacion.y - 3);
 		g.setColor(color);
 		g.fillOval(ubicacion.x, ubicacion.y, 10, 10);
 	}
 
 	public boolean ecuacionDeCirculo(Point punto)
 	{
 		return (((punto.x - ubicacion.x) * (punto.x - ubicacion.x) + (punto.y - ubicacion.y) * (punto.y - ubicacion.y)) <= (RADIO * RADIO));
 	}
 
 	public Point getUbicacion()
 	{
 		return ubicacion;
 	}
  
 	public String getNombre()
 	{
 		return nombre;
 	}
 
  	public void setUbicacion(Point ubicacion)
 	{
 		this.ubicacion = ubicacion;
 	}
 
 	public Color getColorPunto()
 	{
 		return colorPunto;
 	}
 
 	public void setColorPunto(Color colorPunto)
 	{
 		this.colorPunto = colorPunto;
 	}
 
 }

Clase Arista

editar

Permite dibujar una arista entre dos puntos sobre el lienzo. Hace referencia a los puntos.

 import java.awt.Color;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.Point;
 import java.awt.geom.QuadCurve2D; 
 
 public class Arista
 {
 	private Arco arista;
 	private Punto a, b;
 	private Point inicial;
 	private Point terminal, ubicacionExt;
 	private Color color = new Color(0, 128, 128), aux = Color.RED;
 	private int tipo;
 	private float peso;
 
 	public Arista(Punto puntoA, Punto puntoB, int tipo, float peso)
 	{
 		arista = new Arco(puntoA.getNombre(), puntoB.getNombre(), peso);
 
 		a = puntoA;
 		b = puntoB;
 		this.tipo = tipo;
 		this.peso = peso;
 	}
 
 	public void pintarRecta(Graphics ga)
 	{
 		inicial = a.getUbicacion();
 		terminal = b.getUbicacion();
 
 		Graphics2D g = (Graphics2D) ga;
 		double a = (inicial.y - terminal.y);
 		double b = (inicial.x - terminal.x);
 		double m = a / b;
 		double grado = Math.atan(m);
 
 		switch (tipo)
 		{
 			case 0:
 				g.setColor(color);
 				g.drawLine(inicial.x + 5, inicial.y + 5, terminal.x + 5, terminal.y + 5);
 				g.setColor(aux);
 				g.drawString(peso + "", (inicial.x + terminal.x) / 2, (inicial.y + terminal.y) / 2);
 				break;
 
 			case 1:
 				g.setColor(color);
 				g.drawOval(inicial.x - 10, inicial.y - 30, 30, 30);
 				g.setColor(aux);
 				g.drawString(peso + "", inicial.x - 3, inicial.y - 30);
 				break;
 
 			case 2:
 				Point control = null;
 				if (grado > 0)
 				{
 					if (grado <= 45 && grado >= 0)
 						control = new Point((inicial.x + terminal.x) / 2 - 10, (inicial.y + terminal.y) / 2 + 50);
 					if (grado <= 90 && grado > 45)
 						control = new Point((inicial.x + terminal.x) / 2 + 50, (inicial.y + terminal.y) / 2 + 10);
 				}
 				else
 				{
 					if (grado >= -45 && grado <= 0)
 						control = new Point((inicial.x + terminal.x) / 2 - 30, (inicial.y + terminal.y) / 2 - 10);
 					if (grado >= -90 && grado < -45)
 						control = new Point((inicial.x + terminal.x) / 2 - 50, (inicial.y + terminal.y) / 2 - 10);
 				}
 
 				Point tempInicial = new Point(inicial.x + 5, inicial.y + 5),
 				tempFinal = new Point(terminal.x + 5, terminal.y + 5);
 
 				QuadCurve2D.Double quad = new QuadCurve2D.Double();
 
 				quad.setCurve(tempInicial, control, tempFinal);
 
 				g.setColor(aux);
 				g.drawString(peso + "", control.x, control.y);
 				g.setColor(color);
 				g.draw(quad);
 
 				break;
 
 		}
 	}
 
 	public Point getUbicacion()
 	{
 		return ubicacionExt;
 	}
 
 	public int getTipo()
 	{
 		return tipo;
 	}
 
 	public Arco getArista()
 	{
 		return arista;
 	}
 
 	public void setArista(Arco arista)
 	{
 		this.arista = arista;
 	}
 
 	public void getColor()
 	{
 		color = new Color(0, 128, 128);
 		aux = Color.RED;
 	}
 
 	public void setColor(Color color)
 	{
 
 		if (color.equals(new Color(0, 128, 128)))
 			aux = Color.RED;
 		else
 			aux = Color.BLUE;
 
 		this.color = color;
 	}
 }

Paquete Principal

editar

Clase AlgoritmoKruskal

editar
//Implementación del Algoritmo voraz de Kruskal.

 import java.util.ArrayList;
  
 public class AlgoritmoKruskal
 {
 	@SuppressWarnings("unchecked")
 
 	public Grafo aplicarKruskal(Grafo grafo)
 	{
 		Grafo árbol=new Grafo();
 		ArrayList<String> nodos=grafo.getNombres();
 
 		for(int j=0;j<nodos.size();j++)
 		{
 			árbol.ingresarNodo(nodos.get(j));
 		}
 
 		ArrayList<Arco> L=(ArrayList<Arco>)grafo.getAristas().clone();
 
 		Arco pro=L.get(0);
 		árbol.adicionarEnlace(pro.getInicial(), pro.getTerminal(), pro.getPeso());
 		L.remove(pro);
 
 		while(L.size()!=0)
 		{
 			pro=L.get(0);
 
 			if(HayCiclo(árbol, pro,árbol.getNodo(pro.getTerminal()) , pro.getTerminal())==false)
 				árbol.adicionarEnlace(pro.getInicial(), pro.getTerminal(), pro.getPeso());
 
 			L.remove(pro);
 		}
 
 		return árbol;
 	}
 
 	public boolean HayCiclo(Grafo g,Arco aVerificar,Nodo terminal,String N)
 	{
 		ArrayList<Enlace> aux=terminal.getEnlaces();
 
 		if(aux.size()==0)
 			return false;
 
 		if(terminal.existeEnlace(aVerificar.getInicial())!=-1)
 			return true;
 
 		for(int i=0;i<aux.size();i++)
 		{
 			Enlace nodo=aux.get(i);
 
 			if(nodo.getDestino().equals(N)==false)
 				if( HayCiclo(g,aVerificar,g.getNodo(nodo.getDestino()),terminal.getNombre()))
 									return true;
 		}
 
 		return false;
 	}
 }

VENTANA

editar
import java.awt.Color;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class Ventana implements MouseListener, ItemListener, ActionListener, MouseMotionListener
{
       int cantidad = 0;

       ArrayList<Punto> puntos;
       ArrayList<Arista> aristas;
       static JFrame frame;
       JButton aplicar,nuevo,recuperar;
       Container contenedor;
       JRadioButton radioNodo, radioArista, radioMod;
       JLabel ayuda, titulo;
       ButtonGroup grupo;
       JComboBox comboOpcionesRecta,ayudaCombo;
       JScrollPane panel;
       public JTextArea area;
       String ayudaNod,ayudaAri,ayudaMod,ayudaRes,ayudaNue,ayudaApl;
       Punto pun[];
       public Lienzo lienzo;
       Grafo grafo;
       int j,i;
       int x,y;

       private int contador = 0;

       public Ventana()
       {
               frame = new JFrame();
               contenedor=frame.getContentPane();

               Font font=new Font("Verdana",Font.BOLD,11);
               Font font1=new Font("Verdana",Font.PLAIN,12);

               grafo=new Grafo();

               lienzo = new Lienzo();
               lienzo.setBounds(0, 0, 600, 600);
               lienzo.setBorder(BorderFactory.createBevelBorder(1));
               lienzo.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));

               pun = new Punto[2];
               pun[0]=null;
               pun[1]=null;

               puntos = new ArrayList<Punto>();
               aristas = new ArrayList<Arista>();

               area=new JTextArea();
               area.setFont(font1);
               panel=new JScrollPane(area);
               panel.setBounds(660, 270, 280, 250);

               titulo=new JLabel("Dibuje su grafo:");
               titulo.setFont(font);
               titulo.setBounds(610, 20, 130, 20);
               ayuda=new JLabel("Ayuda:");
               ayuda.setBounds(610, 200, 100, 20);
               ayuda.setFont(font);

               comboOpcionesRecta = new JComboBox();
               comboOpcionesRecta.addItem("Arista");
               comboOpcionesRecta.addItem("Arista Bucle");
               comboOpcionesRecta.addItem("Arista Curva");
               comboOpcionesRecta.setFont(font);

               ayudaCombo=new JComboBox();
               ayudaCombo.addItem(">Como hacer un Nodo");
               ayudaCombo.addItem(">Como hacer una Arista");
               ayudaCombo.addItem(">Como modificar un Grafo");
               ayudaCombo.addItem(">Boton 'Nuevo'");
               ayudaCombo.addItem(">Boton 'Aplicar'");
               ayudaCombo.addItem(">Boton'Reset'");
               ayudaCombo.setFont(font);
               ayudaCombo.setSelectedIndex(-1);

               radioNodo = new JRadioButton("Nodo");
               radioArista = new JRadioButton("Arista");
               radioMod = new JRadioButton("Cambiar");

               radioArista.setFont(font);
               radioMod.setFont(font);
               radioNodo.setFont(font);

               grupo = new ButtonGroup();
               grupo.add(radioNodo);
               grupo.add(radioArista);
               grupo.add(radioMod);
               radioNodo.setSelected(true);

               radioNodo.setBounds(680, 60, 120, 20);
               radioArista.setBounds(680, 110, 130, 20);
               radioMod.setBounds(820, 60, 100, 20);

               aplicar=new JButton("Aplicar");
               aplicar.setBounds(670,160, 80, 20);
               aplicar.setFont(font);
               nuevo=new JButton("Nuevo");
               nuevo.setBounds(760,160, 80, 20);
               nuevo.setFont(font);
               recuperar=new JButton("Reset");
               recuperar.setBounds(850,160, 80, 20);
               recuperar.setFont(font);

               comboOpcionesRecta.setBounds(815, 110, 100, 20);
               ayudaCombo.setBounds(710,240,175,20);

               ayudaNod = "";
               ayudaAri = "";
               ayudaMod = "";
               ayudaRes = "";
               ayudaApl = "";
               ayudaNue = "";

               contenedor.setLayout(null);

               contenedor.add(comboOpcionesRecta);
               contenedor.add(radioMod);
               contenedor.add(radioArista);
               contenedor.add(radioNodo);
               contenedor.add(ayudaCombo);
               contenedor.add(lienzo);
               contenedor.add(aplicar);
               contenedor.add(panel);
               contenedor.add(nuevo);
               contenedor.add(recuperar);
               contenedor.add(ayuda);
               contenedor.add(titulo);


               frame.setFont(font);
               frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               frame.setLayout(null);
               frame.setSize(1000, 600);
               frame.setLocation(10, 20);
               frame.setTitle("GrafoMatic Displayer");
               frame.setVisible(true);


               lienzo.addMouseListener(this);
               lienzo.addMouseMotionListener(this);
               aplicar.addActionListener(this);
               nuevo.addActionListener(this);
               recuperar.addActionListener(this);
               radioNodo.addItemListener(this);
               radioArista.addItemListener(this);
               radioMod.addItemListener(this);
               ayudaCombo.addItemListener(this);
       }

       public void mouseClicked(MouseEvent evento)
    {
               if (evento.getClickCount() == 2&&radioNodo.isSelected()==true)
               {

                       String nombre = "hay";

                       do
                       {
                               try
                               {
                                       nombre = JOptionPane.showInputDialog(null,"Ingrese la Etiqueta del Nodo");
                                       nombre.length();
                               }
                               catch(NullPointerException e)
                               {
                                       return;
                               }
                               if(grafo.getNombres().contains(nombre)||nombre==null)
                               {
                                       JOptionPane.showMessageDialog(null,"La Etiqueta es incorrecta, recuerde que no debe haber otra igual","Ingrese de nuevo la Etiqueta",JOptionPane.ERROR_MESSAGE );
                                       nombre="hay";
                               }
                       }
                       while(nombre=="hay");

                       Punto punto = new Punto((int) evento.getPoint().getX() - 5, (int) evento.getPoint().getY() - 5, nombre);
                       grafo.ingresarNodo(nombre);
                       punto.pintarPunto(lienzo.getGraphics());
                       puntos.add(punto);
                       lienzo.setPuntos(puntos);
               }

       }
       public void actionPerformed(ActionEvent e) {

               if(e.getSource()==aplicar)
               {
                       AlgoritmoKruskal nuevo=new AlgoritmoKruskal();

                       Grafo kruskal= new Grafo();
                       kruskal=nuevo.aplicarKruskal(grafo);
                       lienzo.cambiarGrafo(kruskal);
               }
               if(e.getSource()==nuevo)
               {
                       grafo=new Grafo();
                       lienzo.getAristas().clear();
                       lienzo.getPuntos().clear();
                       lienzo.getNeo().clear();
                       aristas.clear();
                       lienzo.punto=false;
                       lienzo.repaint();
               }
               if(e.getSource()==recuperar)
               {
                       for(int i=0;i<lienzo.getNeo().size();i++)
                       {
                               Arista arista=(Arista)lienzo.getNeo().get(i);
                               arista.setColor(new Color(0,128,128));
                       }
                       lienzo.getNeo().clear();
                       lienzo.estado=false;
                       lienzo.repaint();
               }
       }


       public void itemStateChanged(ItemEvent evento)
       {
               if (evento.getSource() == radioNodo)
                       comboOpcionesRecta.setEnabled(false);
               else
                       comboOpcionesRecta.setEnabled(true);
               if(evento.getSource()==radioNodo||radioNodo.isSelected())
                       lienzo.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
               else
                       lienzo.setCursor(Cursor.getDefaultCursor());
               if(evento.getSource()==ayudaCombo)
               {
                       if(ayudaCombo.getSelectedIndex()==0)
                               area.setText(ayudaNod);
                       if(ayudaCombo.getSelectedIndex()==1)
                               area.setText(ayudaAri);
                       if(ayudaCombo.getSelectedIndex()==2)
                               area.setText(ayudaMod);
                       if(ayudaCombo.getSelectedIndex()==3)
                               area.setText(ayudaNue);
                       if(ayudaCombo.getSelectedIndex()==4)
                               area.setText(ayudaApl);
                       if(ayudaCombo.getSelectedIndex()==5)
                               area.setText(ayudaRes);
               }
       }


       public void mousePressed(MouseEvent arg0)
       {
               contador=0;
               if(radioArista.isSelected())
               {
                       for (int i = 0; i < puntos.size(); i++)
                       {
                               if (puntos.get(i).ecuacionDeCirculo(arg0.getPoint()))
                               {
                                       puntos.get(i).setColorPunto(Color.RED);//pintarPunto(lienzo.getGraphics(), Color.RED);
                                       x=puntos.get(i).getUbicacion().x;
                                       y=puntos.get(i).getUbicacion().y;

                                       if(comboOpcionesRecta.getSelectedIndex()==1)
                                       {
                                               pun[contador] = puntos.get(i);
                                               contador++;
                                       }

                                       pun[contador] = puntos.get(i);

                                       break;
                               }
                       }
               }
               if(radioMod.isSelected())
               {
                       for (int i = 0; i < puntos.size(); i++)
                       {
                               if (puntos.get(i).ecuacionDeCirculo(arg0.getPoint()))
                               {
                                       puntos.get(i).setColorPunto(Color.RED);
                                       pun[contador] = puntos.get(i);

                                       break;
                               }
                       }
                       contador=0;
               }
               lienzo.repaint();
       }

       public void mouseReleased(MouseEvent arg0)
       {
               if(radioArista.isSelected())
               {
                       if(pun[1]==null||pun[1].ecuacionDeCirculo(arg0.getPoint())==false||pun[0].getUbicacion().equals(pun[1].getUbicacion()))
                                       contador=0;

                       if(contador==2||comboOpcionesRecta.getSelectedIndex()==1)
                       {
                               float peso=-1;
                               do
                               {
                                       try
                                       {
                                               peso = Float.parseFloat(JOptionPane.showInputDialog(null,"Ingrese el Peso de la Arista"));
                                       }
                                       catch(NumberFormatException ex)
                                       {
                                               JOptionPane.showMessageDialog(null,"El peso de la Arista debe ser un Número","Ingrese de nuevo el Peso",JOptionPane.ERROR_MESSAGE );
                                               peso=-1;
                                       }
                                       catch(NullPointerException e)
                                       {
                                               pun[0].setColorPunto(Color.BLUE);
                                               if(pun[1]!=null)
                                                       pun[1].setColorPunto(Color.BLUE);
                                               lienzo.punto=false;
                                               lienzo.repaint();
                                               return;
                                       }
                               }
                               while(peso==-1);

                               Arista arista=new Arista(pun[0], pun[1], comboOpcionesRecta.getSelectedIndex(),peso);

                               aristas.add(arista);
                               lienzo.setAristas(aristas);

                               arista.pintarRecta(lienzo.getGraphics());
                               grafo.adicionarEnlace(pun[0].getNombre(),pun[1].getNombre(),peso);

                               contador = 0;
                               pun[0].setColorPunto(Color.BLUE);
                               pun[1].setColorPunto(Color.BLUE);

                               lienzo.punto=false;
                               lienzo.repaint();
                       }
               }

               if(pun[0]!=null)
                       pun[0].setColorPunto(Color.BLUE);

               lienzo.repaint();
               lienzo.punto=false;
               contador=0;
               pun[0]=null;
               pun[1]=null;
       }

       public void mouseDragged(MouseEvent e) {

               if(radioArista.isSelected())
               {
                       for (int i = 0; i < puntos.size(); i++)
                       {
                               if (puntos.get(i).ecuacionDeCirculo(e.getPoint()))
                               {
                                       pun[1] = puntos.get(i);
                                       pun[1].setColorPunto( Color.RED);
                                       break;
                               }
                               else
                                       if(pun[1]!=null&&pun[1]!=pun[0])
                                               pun[1].setColorPunto(Color.BLUE);
                       }

                       if(pun[0]!=null)
                       {
                               lienzo.setA(new Point(x,y));
                               lienzo.setB(e.getPoint());

                               lienzo.punto=true;
                               lienzo.repaint();
                       }
                       contador=2;
               }
               if(radioMod.isSelected()&&pun[0]!=null)
               {
                       Point ubicacion=new Point(e.getX()-5,e.getY()-5);
                       pun[0].setUbicacion(ubicacion);
                       lienzo.repaint();
               }
       }

       public void mouseMoved(MouseEvent e) {

               if(radioMod.isSelected())
               {
                       if (puntos.size())      //Aquí debe tener una condición no un solo entero??
                       {
                               if (puntos.get(i).ecuacionDeCirculo(e.getPoint()))
                                       puntos.get(i).pintarPunto(linzo.getGraphics(), Color.RED);
                               else
                                       puntos.get(i).pintarPunto(linzo.getGraphics(), Color.BLUE);
                       }

               }
       }


       public Grafo getGrafo()
       {
               return grafo;
       }


}

Clase Aplicación

editar
// Esta clase es la principal y es la que permite ver corriendo el programa.

 public class Aplicacion {
 
 	public static void main(String args[])
 	{
 		Ventana mi=new Ventana();
 		mi.area.setEditable(false);
        }
 }

Modo de compilación y ejecución

editar

Para poder compilar y ejecutar el programa debe tener instalado en su computador el jdk de java 1.5 o mejores actualizaciones y además debe tener el path de Windows configurado con el jdk. Más sobre configuración del path

Antes de comenzar tiene que copiar cada una de las clases en un bloc de notas separado y guardarlo con el mismo nombre de la clase y con extensión .java. Por ejemplo: al copiar la clase AlgoritmoKruskal en un bloc de notas la guarda como AlgoritmoKruskal.java.

Para compilar abra la consola, o para Windows XP la herramienta símbolo del sistema, estando allí diríjase al directorio donde tiene todas las clases guardadas (Todas las clases deben estar en el mismo directorio para que el programa corra correctamente) y enseguida copie el siguiente comando javac Aplicacion.java, esto le dice a java que compile el main del programa que se encuentra en Aplicacion.java. Si hace esto correctamente deben haberse creado en el directorio de las clases varios archivos .class, uno para cada clase y con el mismo nombre de la clase.

Para ejecutar tiene que haber compilado primero. Nuevamente abra la consola, o símbolo del sistema, y diríjase al directorio donde se encuentran todas las clases y digite el siguiente comando java Aplicacion, esto le dice a java que ejecute el main del programa. Si lo hace correctamente el programa debe estar corriendo.

Pruebas

editar
  En este primer grafo podemos ver que el algoritmo ha seleccionado las aristas con menor peso que son las de 9 y 10.

El algoritmo hace una selección de la menor arista del grafo y la añade a un árbol de manera que al añadirla al árbol no forme un ciclo.

En este ejemplo el árbol que es creado por el algoritmo es el representado por las líneas rojas en la figura b).

  En este segundo ejemplo podemos observar que decisión toma el algoritmo en caso que haya varias aristas con el mismo peso.

El algoritmo sigue el mismo criterio, hace una selección de la menor arista del grafo y la añade a un árbol de manera que al añadirla al árbol no forme un ciclo.

En este ejemplo podemos ver claramente el criterio del que se habla. El algoritmo añadiría al árbol la arista con peso 4, luego la arista con peso 5, luego la que tiene peso 7 y al momento de añadir la arista de peso 8 se da cuenta que si añade la arista e-d forma un ciclo y la desecharía y tomaría luego a a-c, como esta no forma ciclo entonces la añade al árbol.

Para la implementación de este algoritmo también influye el orden en el cual el usuario dibujo las aristas por que el algoritmo pudo haber seleccionado la arista a-c primero, esto lo veremos más claro en el siguiente ejemplo.

El algoritmo sigue el mismo criterio, hace una selección de la menor arista del grafo y la añade a un árbol de manera que al añadirla al árbol no forme un ciclo.

El algoritmo toma en orden ascendente las aristas pero también tiene en cuenta el orden en el cual, el usuario, dibujo las aristas. Para este caso el usuario dibujo primero la arista b-d y luego la c-d, debido a esto el árbol que da como resultado no me toma la arista c-d por que formaría un ciclo en el árbol.

Tiempo empleado

editar

Para una implementación óptima del algoritmo haciendo uso de una interfaz grafica y una estructura adicional se hicieron necesarias alrededor de 15 horas, incluyendo el tiempo que se necesitó para investigar y complementar los conceptos necesarios para lograr el resultado obtenido y deseado.

Tiempo de Ejecución T(n)

editar

Sea T(n) el número de nodos que posee el grafo. Dado que se trata de un grafo denso podemos afirmar que la longitud del camino máximo será n. Igualmente cada llamada recursiva supondrá una disminución del problema en exactamente la unidad. De forma similar, puede afirmarse que el número de llamadas recursivas irá disminuyendo en uno puesto que en cada nivel hay un nodo adyacente más que ya ha sido visitado. Por último puede observarse que dentro del ciclo se realizan al menos n comparaciones. Con los hechos nombrados con anterioridad es posible establecer la siguiente relación de recurrencia: T(n) = (n-1) T(n-1) + n Siendo esta recurrencia no homogénea, es decir, en este caso la combinación lineal no es igual a cero, tal como puede verse en la siguiente expresión: A0tn +a1tn-1 +…..+ aktn-k = bnp(n)

Se procede ahora a definir el valor de b=2 y p(n)=n Obteniendo que el polinomio característico es:

(x-(n-1)) (x-1)² con raíces (n-1) (de multiplicidad 1) y 1 (de multiplicidad 2).

Por lo tanto todas las soluciones son de la forma tn= c1(n-1)n +c21n+c3n1n

Siempre y cuando t0 >=0 para todo n, se concluye inmediatamente que t pertenece a O (n-1)n

Conclusiones

editar
  • El algoritmo de kruskal es sencillo de implementar con las herramientas adecuadas.
  • El algoritmo de kruskal, al momento de implementarlo en algún lenguaje de programación, no es tan óptimo con respecto a otros algoritmos que cumplen el mismo objetivo, como el de Prim, debido que al momento de implementarlo se tiene la limitante de diseñar un algoritmo adicional que encuentre ciclos que en este caso no es el más óptimo.
  • Al momento de implementar el algoritmo es más óptimo ordenar a medida que el usuario ingresa aristas que ordenar después de terminado el grafo. También sería bueno añadir las aristas en una cola de prioridad, ya que se evita igualmente la búsqueda de la arista menor.

Véase también

editar

Enlaces externos

editar