SlideShare a Scribd company logo
1 of 96
Download to read offline
INSTITUTO TECNOLÓGICO
            INSTITUTO TECNOLÓGICO                                        1


                                DE TUXTEPEC
                                DE TUXTEPEC




               Ingeniería en informática

     Portafolio de evidencias
                            Actiiviidad:
                            Act v dad:

                      Temas de la unidad

                            Grupo “A”

Turno:   vespertino
                           Presentado por:

                       Margarita Antonio Gómez



                             Profesor(a):

                      Lic. Tomas Torres Ramírez



                                                  5 de diciembre 20011
Índice                         2




Introducción…………..……………………………………...3
Introducción a las estructuras de datos…………………………………4


Recursividad…………………………………………………………………….………….7

Estructuras lineales……………………………………………………………..…13

Estructuras no lineales………………………………………………………..…43

Métodos de ordenamiento……………………………………………………..63

Métodos de búsqueda…………………………………………………………….68

Análisis de los algoritmos……………………………………………………..……80

Conclusión…………………………………………………………………………………96
INTRODUCCION                   3




En este trabajo hablaremos de todos los
temas del semestre, relacionados con
la carrera de informática, en la materia
estructura de datos.
También      algunos    ejemplos     de
programas en java que complementa lo
aprendido en este semestre.
INTRODUCCIÓN A LAS ESTRUCTURAS                                                    4

            DE DATOS.

     TIPOS DE DATOS ABSTRACTOS (TDA)
     Un TDA es un tipo de dato definido por el programador que se puede
     manipular de un modo similar a los tipos de datos definidos por el sistema.

     Está formado por un conjunto válido de elementos y un número de
     operaciones primitivas que se pueden realizar sobre ellos.


  Ejemplo:
        - Definición del tipo
     Numero racional: Conjunto de pares de elementos (a,b) de tipo entero, con
     b<>0.

MAPA CONCEPTUAL TAD
PROGRAMA RECURSIONES ARCHIVO DE TEXTO                                   5



/*
 * Programa que utiliza metodos recursivos
 * Fibonaci, Factorial, Potencia y Division por estas sucesivas
 */
package packRecursiones;
import javax.swing.*;
public class claseRecursivas {
public static int a, b, n;
claseRecursivas(){
a=0; b=0; n=0;
};
public static int potencia(int b, int n) {
if(n == 0) { //caso base
return 1;
} else {
return b * potencia(b, n -1); // llamada recursiva
}
 }
public static int factorial(int n){
if(n==0) return 1; //Caso base
else return n*factorial(n-1); //Formula recursiva
}
public static int fibonaci( int n){
if(n==1 || n==2) return 1;
else return fibonaci(n-1)+fibonaci(n-2);
}
public static int division( int a, int b)
     {
if(b > a) return 0;
else
return division(a-b, b) + 1;
}
public static void menu(){
String k="";
int e=5, r=0;
k+="Menú de operacionesnn1) Fibonacin2) Factorialn3) potencian";
k+="4) Division por restas sucesivasn5) Salir";
k+="nnElige una opción";
do{
e=Integer.parseInt(JOptionPane.showInputDialog(k));
switch(e){
case 1:
n=Integer.parseInt(JOptionPane.showInputDialog("Valor maximo para fibonaci"));
r=fibonaci(n);
                                                                             6
JOptionPane.showMessageDialog(null,"Los numero fibonaci son "+r);
break;
case 2:
n=Integer.parseInt(JOptionPane.showInputDialog("Valor para obtener el
factorial"));
r=factorial(n);
JOptionPane.showMessageDialog(null,"El factorial es. "+r);
break;
case 3:
b=Integer.parseInt(JOptionPane.showInputDialog("Valor para la base"));
n=Integer.parseInt(JOptionPane.showInputDialog("Valor para potencia"));
r=potencia(b,n);
JOptionPane.showMessageDialog(null,"La potencia es: "+r);
break;
case 4:
 a=Integer.parseInt(JOptionPane.showInputDialog("Valor para dividendo"));
 b=Integer.parseInt(JOptionPane.showInputDialog("Valor para la divisor"));
r=division(b,n);
JOptionPane.showMessageDialog(null,"El resultado de la division es: "+r);
break;
case 5:
JOptionPane.showMessageDialog(null,"Fin del programa");
        }
}while(e!=5);
    };
 public static void main(String[] args) {
 // claseRecursivas obj1=new claseRecursivas();
 claseRecursivas.menu();
    }

}
RECURSIVIDAD                                                 7


                                   RESUMEN

La recursividad es una técnica de programación importante. Se utiliza para realizar
una llamada a una función desde la misma función.

La recursividad consiste en realizar una definición de un concepto en términos del
propio concepto que se está definiendo.

La recursividad forma parte del repertorio para resolver problemas en
Computación y es de los métodos más poderosos y usados.
Los algoritmos recursivos ofrecen soluciones estructuradas, modulares y
elegantemente simples.
La recursividad es un concepto fundamental en matemáticas y en computación.
Una definición recursiva dice cómo obtener conceptos nuevos empleando el
mismo concepto que intenta describir.
En toda situación en la cual la respuesta pueda ser expresada como una
secuencia de movimientos, pasos o transformaciones gobernadas por un conjunto
de reglas no ambiguas, la fórmula recursiva es un buen candidado para resolver el
problema.



Ejemplos:
      Los números naturales se pueden definir de la siguiente forma: 0 es un
      Número natural y el sucesor de un número natural es también un número
      natural.

      El factorial de un número natural n, es 1 si dicho número es 0, o n
      multiplicado por el factorial del número n-1, en caso contrario.

      La n-ésima potencia de un número x, es 1 si n es igual a 0, o el producto de
      x por la potencia (n-1)-ésima de x, cuando n es mayor que 0.

                          Diseño de módulos recursivos.

      Módulo M con una llamada a sí mismo: módulo recursivo directo.

      Módulo M con una llamada a otro F, que hace una llamada a

      M: Módulo recursivo indirecto.

Ejemplo: Implementación del factorial de un número.
public long factorial (long n)
{
                                                                                  8
if (n == 0) return 1;
else return n * factorial(n-1);
}


Búsqueda de soluciones recursivas: cuatro preguntas básicas.

¿Cómo se puede definir el problema en términos de uno o más problemas más
pequeños del mismo tipo que el original?

¿Qué instancias del problema harán de caso base?

Conforme el problema se reduce de tamaño ¿se alcanzará el caso base?

¿Cómo se usa la solución del caso base para construir una solución correcta al
problema original?

Ejemplo:




                        La pila del ordenador y la recursividad.

La memoria de un ordenador a la hora de ejecutar un programa queda dividida en
dos partes:

·la zona donde se almacena el código del programa y

·la zona donde se guardan los datos: pila (utilizada para llamadas recursivas).
9
Programa principal llama a una rutina M, se crea en la pila de un registro de
activación o entorno, E almacena constantes, variables locales y parámetros
formales. Estos registros se van apilando conforme se llama sucesivamente desde
una función a otras.

Cuando finaliza la ejecución, se va liberando el espacio

Profundidad de la recursión: número de registros de activación en la pila en un
momento dado.

Problema:
Si profundidad es muy grande => desbordamiento pila.
Representación gráfica del registro de activación:

Ejemplo de evolución de la pila: impresión de palabras en sentido contrario al
leído.

Función recursiva: imp_OrdenInverso.

Parámetros: número de palabras que quedan por leer (n).

Respuestas:
1. Se lee la primera palabra, y recursivamente se llama a la función para que lea e
imprima el resto de palabras, para que finalmente se imprima la primera leída.
2. El caso base: cuando sólo quede una palabra (n== 1), en cuyo caso se
imprimirá directamente.
3. Como en cada llamada recursiva hay que leer una palabra menos del total, en
n-1 llamadas se habrá alcanzado el caso base.
4. Una vez se haya alcanzado el caso base al devolver continuamente el control a
las funciones invocantes se irán imprimiendo de atrás a delante las palabras
obtenidas desde la entrada estándar.

public void imp_OrdenInverso(int n)
{
String palabra;
if (n == 1)
{
palabra =Console.in.readln();
System.out.println(palabra);
}
else
{
palabra =Console.in.readln();
imp_OrdenInverso(n-1);
Las definiciones recursivas de funciones en matemáticas que tienen como
                                                                                            10
argumentos números enteros, se llaman relaciones de recurrencia.
Forma de una ecuación de recurrencia: coar +c1ar-1 + c2ar-2 + ....+ ckar-k = f(r) función
matemática discreta donde ci son constantes, es llamada una ecuación de
recurrencia de coeficientes constantes de orden k, condicionada a que c 0 y ck = 0.

Una definición recursiva dice cómo obtener conceptos nuevos empleando el
mismo concepto que intenta definir.

El poder de la recursividad es que los procedimientos o conceptos complejos
pueden expresarse de una forma simple.

Un razonamiento recursivo tiene dos partes: la base y la regla recursiva de
construcción. La base no es recursiva y es el punto tanto de partida como de
terminación de la definición.

Ejemplo:
Base: La secuenciación, iteración condicional y selección son estructuras válidas
de control que pueden ser consideradas como enunciados.

Regla recursiva: Las estructuras de control que se pueden formar combinando de
manera válida la secuenciación iteración condicional y selección también son
válidos.

Un conjunto de objetos está definido recursivamente siempre que:
(B) algunos elementos del conjunto se especifican explícitamente

1. El procedimiento se llama a si mismo
2. El problema se resuelve, resolviendo el mismo problema pero de tamaño menor
3. La manera en la cual el tamaño del problema disminuye asegura que el caso
base eventualmente se alcanzará

La recursividad es un método poderoso usado en inteligencia artificial, su poder es
que algunos conceptos complejos pueden expresarse en una forma simple. Una
definición recursiva difiere de una definición circular en que tiene una forma de
escapar de su expansión infinita. Este escape se encuentra en la definición o
porción no recursiva o terminal de la definición.

Las fórmulas recursivas pueden aplicarse a situaciones tales como prueba de
teoremas, solución de problemas combinatorios, algunos acertijos, etc.

 Cuando una llamada recursiva es la última posición ejecutada del procedimiento
se llama recursividad de cola, recursividad de extremo final o recursión e extremo
de cola.
Cuando un procedimiento incluye una llamada a si mismo se conoce como
recursión directa.
11
 Cuando un procedimiento llama a otro procedimiento y este causa que el
procedimiento original sea invocado, se conoce como recursión indirecta.

Al principio algunas personas se sienten un poco incómodas con la recursividad,
tal vez porque da la impresión de ser un ciclo infinito, pero en realidad es menos
peligrosa una recursión infinita que un ciclo infinito, ya que una recursividad infinita
pronto se queda sin espacio y termina el programa, mientras que la iteración
infinita puede continuar mientras no se termine en forma manual.
Cuando un procedimiento recursivo se llama recursivamente a si mismo varias
veces, para cada llamada se crean copias independientes de las variables
declaradas en el procedimiento.



            PROGRAMA CON MENÚ Y MÉTODOS RECURSIVOS


               Programa que llama a los métodos recursivos



package packRecursiones;

import javax.swing.*;

public class claseRecursivas {

public static int a, b, n;

claseRecursivas(){

a=0; b=0; n=0;

};

public static int potencia(int b, int n) {

if(n == 0) { //caso base

return 1;

} else {

return b * potencia(b, n -1); // llamada recursiva

}
}
                                                                         12
public static int factorial(int n){

if(n==0) return 1; //Caso base

else return n*factorial(n-1); //Formula recursiva

}

public static int fibonaci( int n){

if(n==1 || n==2) return 1;

else return fibonaci(n-1)+fibonaci(n-2);

}

public static int division( int a, int b)

{

if(b > a) {

return 0;

}

else {

return division(a-b, b) + 1;

}

}

public static void menu(){

String k="";

int e=5, r=0;

k+=" MENU DE OPERACIONESnn1) Fibonacin2) Factorialn3) potencian4)
Division por restas sucesivasn5) SalirnnElige una opción";

do{

e=Integer.parseInt(JOptionPane.showInputDialog(k));

switch(e){
case 1:
                                                                                 13
n=Integer.parseInt(JOptionPane.showInputDialog("Valor maximo para fibonaci"));

r=fibonaci(n);

JOptionPane.showMessageDialog(null,"Los numero fibonaci son: "+r);

break;

case 2:

n=Integer.parseInt(JOptionPane.showInputDialog("Valor para obtener el
factorial"));

r=factorial(n);

JOptionPane.showMessageDialog(null,"El factorial de "+n+"!="+r);

break;

case 3:

b=Integer.parseInt(JOptionPane.showInputDialog("Valor para la base"));

n=Integer.parseInt(JOptionPane.showInputDialog("Valor para potencia"));

r=potencia(b,n);

JOptionPane.showMessageDialog(null,"La potencia es: "+r);

break;

case 4:

b=Integer.parseInt(JOptionPane.showInputDialog("Valor para dividendo"));

n=Integer.parseInt(JOptionPane.showInputDialog("Valor para la divisor"));

r=division(b,n);

JOptionPane.showMessageDialog(null,"El resultado de la division es: "+r);

break;

case 5:

JOptionPane.showMessageDialog(null,"Fin del programa... :)");

}
}while(e!=5);
                                                       14
};

public static void main(String[] args) {

// claseRecursivas obj1=new claseRecursivas();

claseRecursivas.menu();

}

}



                                           PANTALLAS
15
ESTRUCTURAS LINEALES                                                16


                          TRABAJO DE INVESTIGACION

                                   INTRODUCCIÓN


Las estructuras lineales son importantes porque aparecen con mucha frecuencia
en situaciones de la vida: Una cola de clientes de un banco, las instrucciones de
un programa, los caracteres de una cadena o las páginas de un libro


Características: existe un único elemento, llamado primero, existe un único
elemento, llamado último, cada elemento, excepto el primero, tiene un único
predecesor y cada elemento, excepto el último, tiene un único sucesor.


Operaciones: crear la estructura vacía, insertar un elemento, borrar y obtener un
elemento. Para definir claramente el comportamiento de la estructura es necesario
determinar en qué posición se inserta un elemento nuevo y qué elemento se borra
o se obtiene.



Principales estructuras lineales: pilas, colas y listas.
ESTRUCTURAS LINEALES
                                                                                         17

                           Listas (estructura de datos)


Una lista enlazada es una de las estructura de datos fundamentales, y puede ser
usada para implementar otras estructuras de datos. Consiste en una secuencia de
nodos, en los que se guardan campos de datos arbitrarios y una o dos referencias
(punteros) al nodo anterior y/o posterior. El principal beneficio de las listas
enlazadas respecto a los array convencionales es que el orden de los elementos
enlazados puede ser diferente al orden de almacenamiento en la memoria o el
disco, permitiendo que el orden de recorrido de la lista sea diferente al de
almacenamiento.


Una lista enlazada es un tipo de dato auto-referenciado porque contienen un
puntero o link a otro dato del mismo tipo. Las listas enlazadas permiten
inserciones y eliminación de nodos en cualquier punto de la lista en tiempo
constante (suponiendo que dicho punto está previamente identificado o
localizado), pero no permiten un acceso aleatorio. Existen diferentes tipos de listas
enlazadas: Lista Enlazadas Simples, Listas Doblemente Enlazadas, Listas
Enlazadas     Circulares    y    Listas    Enlazadas      Doblemente       Circulares.
Lenguajes imperativos u orientados a objetos tales como C o C++ y Java,
respectivamente,    disponen    de   referencias   para   crear   listas   enlazadas.



                           Operaciones básicas con listas




El alterador de lista es un objeto que permite recorrer los elementos de la lista y
operar con ella
Dispone de un cursor que se sitúa entre dos elementos: previo ypróximo
                                                                                        18


Las operaciones básicas de las listas son:
• Constructor: Crea la lista vacía
• hazNula: Elimina todos los elementos de la lista, dejándolo vacía
• estaVacia: Si la lista está vacía retorna true. En otro caso, retorna false
• Tamaño: Retorna un entero que dice cuántos elementos hay en la lista
• iteradorDeLista: Retorna un iterador de lista asociado a la lista, para poder
recorrer sus elementos
Operaciones básicas de los iteradores de lista




Inicialmente el iterador de lista tiene el cursor situado antes del primer elemento


Las operaciones básicas que se pueden hacer con un iterador de lista son:


       añade: Añade elElemento a la lista entre los elementos previo y próximo, si
       existen; si no existen, lo añade como el único elemento de la lista. El cursor
       queda situado justo después del nuevo elemento.
       Borra: borra el último elemento obtenido de la lista con previo() o proximo().
       Si no hay tal elemento, lanza estadoIncorrecto.
       cambiaElemento: modifica el último elemento obtenido de la lista con
       previo() o proximo() sustituyéndolo por el nuevoElemento. Si no hay tal
       elemento, o si se ha llamado a borra () o añade() después de previo() o
       proximo(), lanza estadoIncorrecto.
hayPrevio: Retorna true si existe un elemento previo al cursor y false en
                                                                                      19
        caso contrario.
        hayProximo: Retorna true si existe un elemento proximo al cursor y false en
        caso contrario.
        Previo: Retorna el elemento previo al cursor, y hace que éste retroceda un
        elemento en la lista. Si no existe elemento previo, lanza noExiste.
        Próximo: Retorna el elemento próximo al cursor, y hace que éste avance un
        elemento en la lista. Si no existe elemento próximo, lanza noExiste.
        Observar que si se llama a previo () después de llamar a proximo() se
        obtiene el mismo elemento.


La interfaz List de Java
public interface List<E> extends Collection<E>
{
// Positional access
E get(int index);
E set(int index, E element); //optional
boolean add(E element); //optional
void add(int index, E element); //optional
E remove(int index); //optional
boolean addAll(int index,
Collection<? extends E> c); //optional
// Search
int indexOf(Object o);
int lastIndexOf(Object o);
// Iteration
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
// Range-view
List<E> subList(int from, int to);
}
Tipos de listas
                                                                                       20

                               Listas simples enlazadas



La lista enlazada básica es la lista enlazada simple la cual tiene un enlace por
nodo. Este enlace apunta al siguiente nodo en la lista, o al valor NULL o a la lista
vacía, si es el último nodo.




Una lista enlazada simple contiene dos valores: el valor actual del nodo y un
enlace al siguiente nodo




                               Lista Doblemente Enlazada


Un tipo de lista enlazada más sofisticado es la lista doblemente enlazada o lista
enlazadas de dos vías. Cada nodo tiene dos enlaces: uno apunta al nodo anterior,
o apunta al valor NULL o a la lista vacía si es el primer nodo; y otro que apunta al
siguiente nodo siguiente, o apunta al valor NULL o a la lista vacía si es el último
nodo.




Una lista doblemente enlazada contiene tres valores: el valor, el link al nodo
siguiente, y el link al anterior

En algún lenguaje de muy bajo nivel, XOR-Linking ofrece una vía para
implementar listas doblemente enlazadas, usando una sola palabra para ambos
enlaces, aunque el uso de esta técnica no se suele utilizar.
21
                             Listas enlazadas circulares


En una lista enlazada circular, el primer y el último nodo están unidos juntos. Esto
se puede hacer tanto para listas enlazadas simples como para las doblemente
enlazadas. Para recorrer un lista enlazada circular podemos empezar por
cualquier nodo y seguir la lista en cualquier dirección hasta que se regrese hasta
el nodo original. Desde otro punto de vista, las listas enlazadas circulares pueden
ser vistas como listas sin comienzo ni fin. Este tipo de listas es el más usado para
dirigir buffers para “ingerir” datos, y para visitar todos los nodos de una lista a partir
de uno dado.




Una lista enlazada circular que contiene tres valores enteros




                        Listas enlazadas circulares simples


Cada nodo tiene un enlace, similar al de las listas enlazadas simples, excepto que
el siguiente nodo del último apunta al primero. Como en una lista enlazada simple,
los nuevos nodos pueden ser solo eficientemente insertados después de uno que
ya tengamos referenciado. Por esta razón, es usual quedarse con una referencia
solamente al último elemento en una lista enlazada circular simple, esto nos
permite rápidas inserciones al principio, y también permite accesos al primer nodo
desde el puntero del último nodo.




                        Lista Enlazada Doblemente Circular
22
En una lista enlazada doblemente circular, cada nodo tiene dos enlaces, similares
a los de la lista doblemente enlazada, excepto que el enlace anterior del primer
nodo apunta al último y el enlace siguiente del último nodo, apunta al primero.
Como en una lista doblemente enlazada, las inserciones y eliminaciones pueden
ser hechas desde cualquier punto con acceso a algún nodo cercano. Aunque
estructuralmente una lista circular doblemente enlazada no tiene ni principio ni fin,
un puntero de acceso externo puede establecer el nodo apuntado que está en la
cabeza o al nodo cola, y así mantener el orden tan bien como en una lista
doblemente enlazada.



                            Aplicaciones de las listas


Las listas enlazadas son usadas como módulos para otras muchas estructuras de
datos, tales como pilas, colas y sus variaciones.
El campo de datos de un nodo puede ser otra lista enlazada. Mediante este
mecanismo, podemos construir muchas estructuras de datos enlazadas con listas;
esta practica tiene su origen en el lenguaje de programación Lisp, donde las listas
enlazadas son una estructura de datos primaria (aunque no la única), y ahora es
una   característica   común     en    el   estilo   de   programación     funcional.
A veces, las listas enlazadas son usadas para implementar arrays asociativos, y
estas en el contexto de las llamadas listas asociativas. Hay pocas ventajas en este
uso de las listas enlazadas; hay mejores formas de implementar éstas estructuras,
por ejemplo con árboles binarios de búsqueda equilibrados. Sin embargo, a veces
una lista enlazada es dinámicamente creada fuera de un subconjunto propio de
nodos semejante a un árbol, y son usadas más eficientemente para recorrer ésta
serie de datos.
BIBLIOGRAFIA
                                                                        23

Cómo programar en Java - Página 978

Harvey M. Deitel, Paul J. Deitel, Guillermo Trujano Mendoza – 2004




http://estdatosgrupo8a.blogspot.com/2009/03/estructuras-lineales.html
24



                              PROGRAMA DE LISTAS

Subir el programa de listas armado con los métodos que están en
la presentación.




         Programa:

         :
import javax.swing.JOptionPane;

public class ListEnlaza{

static ListEnlaza accion=new ListEnlaza();

int n=Integer.parseInt(JOptionPane.showInputDialog("TAMAÑO"));

public static void main(String[] args) {

int opc=0;

while(true){

opc=Integer.parseInt(JOptionPane.showInputDialog(null,"__¿ACCION A
REALIZAR?__nn"+"1. INCERTAR AL INICIOn"+"2. INCERTAR AL FINALn"+"3. BORRAR
AL FINAL n"+"4.BORRAR AL INICIO n"+"5. TAMAÑO DE LA LISTA n"+"6. DATOS DE LA
LISTA FINALn"+"7. DATOS DE LA LISTA INICIO n"+"8. SALIRn"));

switch(opc){

case 1: accion.Insercion();

break;
case 2: accion.addLast(100);
                                                                         25
break;

case 3: accion.deleteLast();

break;

case 4: accion.Eliminar();

break;

case 5: accion.tamano();

break;

 case 6: accion.imprimir();

 break;

  case 7: accion.Recorrido();

 break;

  case 8: System.exit(0);

  break;

default: JOptionPane.showMessageDialog(null,"OPCION INVALIDA","ERROR",

JOptionPane.ERROR_MESSAGE);;

break;}} }

public class Nodo {

Nodo nodoDer;

  int dato;

   public Nodo(int dato) {

       this.dato = dato;

       this.nodoDer = null;

  }}
private Nodo primero;
                                                                                26
  private Nodo ultimo;

  private int tamano;

public ListEnlaza() {

      this.primero = null;

      this.ultimo = null;

      this.tamano = 0;

  }

//Metodo utilizado para denotar que la lista se encuentra vacia.

  public boolean siVacio() {

      return (this.primero == null);

  }




//Metodo para agregar al inicio

String LIS[]=new String [n];

        int Final=-1;

        int Frente=0;

        int Max=LIS.length-1;

        public void Insercion()throws ArrayIndexOutOfBoundsException{

               if(Final==Max){
        JOptionPane.showMessageDialog(null,"¡DESBORDAMIENTO, LLENA!","ERROR",

                JOptionPane.ERROR_MESSAGE);;

                }
else {
                                                                                   27
                        Frente=-1;

                        Final++;

       LIS[Final]=JOptionPane.showInputDialog(null,"INSERTA UN DATO:");

                        JOptionPane.showMessageDialog(null,"SE INSERTO EL DATO (
"+LIS[Final]+" )");

               }

       }

public void Eliminar()throws ArrayIndexOutOfBoundsException{

               if(Final==-1)

               JOptionPane.showMessageDialog(null,"¡VACIA","ERROR!",

               JOptionPane.ERROR_MESSAGE);

               else{

            JOptionPane.showMessageDialog(null, "¡DATO ("+LIS[ 0 ]+")
ELIMINADO!");

       for (int i=0; i<Final-1; i++)

                                LIS[ i ] = LIS[ i+1 ];

                                Final--;}}

       public void Recorrido()throws ArrayIndexOutOfBoundsException{

               if(Final==-1){

               JOptionPane.showMessageDialog(null,"COLA VACIA","ERROR",

               JOptionPane.ERROR_MESSAGE);

               }

               else{

               String Recorrido= "";
Recorrido+= "n ELEMENTOS EN COLA:";
                                                                                           28
                                        for(int i=0; i<=Final; i++)

                                        Recorrido+= "nLISTA["+i+"] = "+LIS[i];

                                        JOptionPane.showMessageDialog(null, Recorrido);

                  }         }

//Metodo para agregar al final de la lista.

  public ListEnlaza addLast(int dato) {

                  dato=Integer.parseInt(JOptionPane.showInputDialog("Ingresa un dato"));

          if(siVacio()) {

          Nodo nuevo = new Nodo(dato);

          primero = nuevo;

          ultimo = nuevo;

          nuevo.nodoDer = nuevo;

      }

      else {

          Nodo nuevo = new Nodo(dato);

          nuevo.nodoDer = null;

          ultimo.nodoDer = nuevo;

          ultimo = nuevo;

      }

            this.tamano++;

      return this;

  }
//Metodo para borrar al final de la lista.
                                                                                 29
  public Nodo deleteLast() {

    Nodo eliminar = null;

    if(siVacio()) {

      JOptionPane.showMessageDialog(null,"LA LISTA SE ENCUENTRA
VACIA","ERROR",

                 JOptionPane.ERROR_MESSAGE);

        return null;

    }

    if(primero == ultimo) {

        primero = null;

        ultimo = null;

    }

    else {

        Nodo actual = primero;

        while(actual.nodoDer != ultimo) {

            actual = actual.nodoDer;

        }

        eliminar = actual.nodoDer;

        actual.nodoDer = null;



        ultimo = actual;

    }

       JOptionPane.showMessageDialog(null,"SE ELIMINO EL DATO DE LA POSICION (
"+tamano+" )");
this.tamano--;
                                                                                   30
          return eliminar;

          }

//Metodo que imprime el tamaño de la lista.

  public void tamano() {

          JOptionPane.showMessageDialog(null, "El tamaño es:n " + this.tamano);

  }

//Metodo que imprime la lista y los valores ingresados.

  public Nodo imprimir() {

              Nodo imprimir = null;

          if(siVacio()) {

      JOptionPane.showMessageDialog(null,"LA LISTA SE ENCUENTRA
VACIA","ERROR",

                       JOptionPane.ERROR_MESSAGE);

              return null;

          }

          if(tamano != 0) {

              Nodo temp = primero;

              String str = "";

              for(int i = 0; i < this.tamano; i++) {

                  str = str + temp.dato + "n";

                  temp = temp.nodoDer;

              }

              JOptionPane.showMessageDialog(null, "Lista:n" + str);

      }
return imprimir;
                       31
       }

}



     Pantallas:
32
Hacer un programa que utilice las operaciones sobre las pilas o
colas (push, pop y recorrido).



import javax.swing.*;

public class Colas {

  static Cola accion=new Cola();

  public static void main(String[] args) {

             int opc=0;

             while(true){


     opc=Integer.parseInt(JOptionPane.showInputDialog(null,"__¿ACCION A
REALIZAR?__nn" +"1. INSERCIÓNn"+"2. RECORRIDOn"+"3. BUSCARn"+"4.
ELIMINAR n"+"5. SALIRn"));

                    switch(opc){

                    case 1: accion.Insercion();

                    break;

                    case 2: accion.Recorrido();

                    break;

                    case 3: accion.Buscar();

                break;

                case 4: accion.Eliminar();

                break;

                case 5: System.exit(0);

                break;

                default:           JOptionPane.showMessageDialog(null,"OPCION
INVALIDA","ERROR",
JOptionPane.ERROR_MESSAGE);;
                                                                               33
                       break;

                       }

              }

       }

}



class Cola{

    int n=Integer.parseInt(JOptionPane.showInputDialog("INTRODUCE         EL
TAMAÑO DE LA COLA"));

      String cola[]=new String [n];

      int Final=-1;

      int Frente=0;

      int Max=cola.length-1;

      public void Insercion()throws ArrayIndexOutOfBoundsException{

           if(Final==Max){
     JOptionPane.showMessageDialog(null,"¡DESBORDAMIENTO,               COLA
LLENA!","ERROR",

              JOptionPane.ERROR_MESSAGE);;

              }

              else {

                       Frente=-1;

                       Final++;

      cola[Final]=JOptionPane.showInputDialog(null,"INSERTA UN DATO:");

                   JOptionPane.showMessageDialog(null,"SE     INSERTO     EL
DATO ( "+cola[Final]+" )");

              }
}
                                                                                      34
      public void Recorrido()throws ArrayIndexOutOfBoundsException{

              if(Final==-1){

              JOptionPane.showMessageDialog(null,"COLA VACIA","ERROR",

              JOptionPane.ERROR_MESSAGE);

              }

              else{

              String Recorrido= "";

                       Recorrido+= "n ELEMENTOS EN COLA:";

                                     for(int i=0; i<=Final; i++)

                                     Recorrido+= "ncola["+i+"] = "+cola[i];

                                     JOptionPane.showMessageDialog(null,
Recorrido);

              }

      }

      public void Buscar()throws ArrayIndexOutOfBoundsException{

      int ban=0;

              String encontrado="";

              String       elemento=JOptionPane.showInputDialog(null,"¿QUE     DATO
BUSCAS?");

              for(int i=0;i<=Final;i++){

                       if(elemento.equals(cola[i])){

                       encontrado=encontrado+cola[i];

                       ban=ban+1;

                       }

              }
if(ban==0){
                                                                                           35
             JOptionPane.showMessageDialog(null,"DATO ("+elemento+")
NO ENCONTRADO ","ERROR",

            JOptionPane.ERROR_MESSAGE);

            }

            else{

        JOptionPane.showMessageDialog(null,                      ""+ban+"       DATO
ENCONTRADO= ( "+elemento+" )");

      }

}

public void Eliminar()throws ArrayIndexOutOfBoundsException{

            if(Final==-1)

            JOptionPane.showMessageDialog(null,"¡COLA VACIA","ERROR!",

            JOptionPane.ERROR_MESSAGE);



            else{

          JOptionPane.showMessageDialog(null,               "¡DATO   ("+cola[   0   ]+")
ELIMINADO!");

                            for (int i=0; i<Final-1; i++)

                            cola[ i ] = cola[ i+1 ];

                            Final--;

            }

      }



}
Programa de pila personalizado   36




mport javax.swing.*;

public class Pila2{

       class Nodo {

              int info;

              Nodo sig;



       }

private Nodo raiz;

public Pila2 (){

       raiz=null;

       }

       public void insertar(int x){

              Nodo nuevo;

              nuevo=new Nodo ();

              nuevo.info=x;

              if(raiz==null)

              {

                      nuevo.sig=null;

                      raiz=nuevo;

                      }

                      else

                      {
nuevo. sig=raiz;
                                                                           37
              raiz=nuevo;

              }

       }

       public int extraer ()

       {

              if(raiz!=null)

       {

       int informacion=raiz. info;

       raiz = raiz. sig;

       return informacion;



       }

       else

       {

              return Integer.MAX_VALUE;



       }

}

public void imprimir (){

       Nodo reco=raiz;

       System.out.println("listado de todos los elementos de la pila.");

       while(reco!=null){

              System.out.print(reco.info+"-");

              reco=reco.sig;
}
                                                                                    38
                    System.out.println();

              }

      public static void main (String[] ar)        {



                    Pila2 pila1=new Pila2();

                    int op=4,dato;

                    do

                    {

                     String     menu         ="nn      menu       de   opciones
nn1)Insertarn2)Eliminarn3)Listan4)SalirnnElije una opcion:";

                    op=Integer.parseInt(JOptionPane.showInputDialog(menu));

                    switch(op){

                         case                                                  1:
dato=Integer.parseInt(JOptionPane.showInputDialog("Dato a insertar"));



                           pila1.insertar(dato);

                           break;

                           case 2:dato=pila1.extraer();

                           JOptionPane.showMessageDialog(null,"El            dato
eliminado es:"+dato);

                           break;

                           case 3:pila1.imprimir();

                           break;

                           case 4:JOptionPane.showMessageDialog(null,"Fin del
programa");

                           }
}
                                                                                         39
                              while(op!=4)

}

}




Hacer un programa que manipule una cola. Debe tener los métodos para
insertar, eliminar y recorrer la cola en una lista enlazada.

Colas


Las colas son una subclase de las listas lineales que siguen el
orden FIFO (First Input First Output - Primero en entrar Primero en
salir). Este orden es que siguen las filas que hacemos en la vida
cotidiana al ir al banco, las tortillas, etc. En las colas la inserción
se hace al final y la eliminación se hace al frente, por lo que se
hace necesario el uso de una variable Primero y otra variable
Último por medio de las cuales se llevaran a cabo las operaciones.




Import javax.swing.*;

public class colas1 {

    static cola accion=new cola();

    public static void main(string[] args) {

               int opc=0;

               while(true){


       opc=integer.parseint(joptionpane.showinputdialog(null,"__¿accion              a
realizar?__nn" +"1. Insertarn"+"2. Recorrern"+"3. Eliminar n"+"4. Salirn"));
switch(opc){
                                                                                      40
                       case 1: accion.insercion();

                       break;

                       case 2: accion.recorrido();

                       break;

                  case 4: accion.eliminar();

                  break;

                  case 5: system.exit(0);

                  break;

                       default:          joptionpane.showmessagedialog(null,"opcion
invalida","error",

              joptionpane.error_message);;

                       break;

                       }

              }

        }

}



Class cola{

       int n=integer.parseint(joptionpane.showinputdialog("introduce el tamaño de
la cola"));

       string cola[]=new string [n];

       int final=-1;

       int frente=0;

       int max=cola.length-1;

       public void insercion()throws arrayindexoutofboundsexception{
if(final==max){
                                                                                         41
       joptionpane.showmessagedialog(null,"¡desbordamiento, cola llena!","error",

               joptionpane.error_message);;

               }

               else {

                        frente=-1;

                        final++;

       cola[final]=joptionpane.showinputdialog(null,"inserta un dato:");

                        joptionpane.showmessagedialog(null,"se inserto el dato (
"+cola[final]+" )");

               }

       }

       public void recorrido()throws arrayindexoutofboundsexception{

               if(final==-1){

               joptionpane.showmessagedialog(null,"cola vacia","error",

               joptionpane.error_message);

               }

               else{

               string recorrido= "";

                        recorrido+= "n elementos en cola:";

                                       for(int i=0; i<=final; i++)

                                       recorrido+= "ncola["+i+"] = "+cola[i];

                                       joptionpane.showmessagedialog(null, recorrido);

               }

       }
Public void eliminar()throws arrayindexoutofboundsexception{
                                                                                               42
                if(final==-1)

                joptionpane.showmessagedialog(null,"¡cola vacia","error!",

                joptionpane.error_message);



                else{

                joptionpane.showmessagedialog(null,             "¡dato   ("+cola[   0   ]+")
eliminado!");

                                for (int i=0; i<final-1; i++)

                                cola[ i ] = cola[ i+1 ];

                                final--;

                }

      }



}
ESTRUCTURAS NO LINEALES                                                        43


Arboles

                       Concepto de arboles
Un árbol se define como una colección de nodos organizados en forma recursiva.
Cuando hay 0 nodos se dice que el árbol está vacío, en caso contrario el árbol
consiste en un nodo denominado raíz, el cual tiene 0 o más referencias a otros
árboles, conocidos como subárboles. Las raíces de los subárboles se denominan
hijos de la raíz, y consecuentemente la raíz se denomina padre de las raíces de
sus subárboles. Una visión gráfica de esta definición recursiva se muestra en la
siguiente figura:




Los nodos que no poseen hijos se denominan hojas. Dos nodos que tienen el
padre en común se denominan hermanos.

Un camino entre un nodo n1 y un nodo nk está definido como la secuencia de
nodos n1, n2,..., nk tal que ni es padre de ni+1, 1 <= i < k. El largo del camino es el
número de referencias que componen el camino, que para el ejemplo son k-1.
Existe un camino desde cada nodo del árbol a sí mismo y es de largo 0. Nótese
que en un árbol existe un único camino desde la raíz hasta cualquier otro nodo del
árbol. A partir del concepto de camino se definen los conceptos de ancestro y
descendiente: un nodo n es ancestro de un nodo m si existe un camino desde n a
m; un nodo n es descendiente de un nodo m si existe un camino desde m a n.

Se define la profundidad del nodo nk como el largo del camino entre la raíz del
arbol y el nodo nk. Esto implica que la profundidad de la raíz es siempre 0. La
altura de un nodo nk es el máximo largo de camino desde nk hasta alguna hoja.
Esto implica que la altura de toda hoja es 0. La altura de un árbol es igual a la
altura de la raíz, y tiene el mismo valor que la profundidad de la hoja más
profunda. La altura de un árbol vacío se define como -1.

La siguiente figura muestra un ejemplo de los conceptos previamente descritos:
44




      A es la raíz del árbol.
      A es padre de B, C y D.
      E y F son hermanos, puesto que ambos son hijos de B.
      E, J, K, L, C, P, Q, H, N y O son las hojas del árbol.
      El camino desde A a J es único, lo conforman los nodos A-B-F-J y es de
      largo 3.
      D es ancestro de P, y por lo tanto P es descendiente de D.
      L no es descendiente de C, puesto que no existe un camino desde C a L.
      La profundidad de C es 1, de F es 2 y de Q es 4.
      La altura de C es 0, de F es 1 y de D es 3.
      La altura del árbol es 4 (largo del camino entre la raíz A y la hoja más
      profunda, P o Q).



                       Clasificación de árboles binarios:

1.-arbol binario distinto: Se dice que un árbol es distinto cuando su estructura
grafica es diferente.

2.-arbol binario similar.- Se dice que un árbol es similar cuando su estructura
grafica es idéntica pero la información que contiene entre sus nodos es diferente.

3.-arbol binario equivalente.-Son aquellos que su estructura grafica es idéntica
pero además la información entre sus nodos.

4.-arbol binario completo.-son aquellos que todos nus nodos excepto el último
nivel tienen sus dos hijos.

5.-arbol binario lleno: Es aquel que tiene su número máximo de posibles nodos.


Operaciones básicas sobre arboles binarios


Árboles binarios
Un árbol binario es un árbol en donde cada nodo posee 2 referencias a subárboles
                                                                                      45
(ni más, ni menos). En general, dichas referencias se denominan izquierda y
derecha, y consecuentemente se define el subárbol izquierdo y subárbol derecho
del arbol.




Como en toda estructura de datos hay dos operaciones básicas, inserción y
eliminación.

Inserción

El procedimiento de inserción en un árbol binario de búsqueda es muy sencillo,
únicamente hay que tener cuidado de no romper la estructura ni el orden del árbol.

Cuando se inserta un nuevo nodo en el árbol hay que tener en cuenta que cada
nodo no puede tener más de dos hijos, por esta razón si un nodo ya tiene 2 hijos,
el nuevo nodo nunca se podrá insertar como su hijo. Con esta restricción nos
aseguramos mantener la estructura del árbol, pero aún nos falta mantener el
orden.

Para localizar el lugar adecuado del árbol donde insertar el nuevo nodo se realizan
comparaciones entre los nodos del árbol y el elemento a insertar. El primer nodo
que se compara es la raíz, si el nuevo nodo es menor que la raíz, la búsqueda
prosigue por el nodo izquierdo de éste. Si el nuevo nodo fuese mayor, la búsqueda
seguiría por el hijo derecho de la raíz.

Este procedimiento es recursivo, y su condición de parada es llegar a un nodo que
no tenga hijo en la rama por la que la búsqueda debería seguir. En este caso el
nuevo nodo se inserta en ese hueco, como su nuevo hijo.

Vamos a verlo con un ejemplo sobre el siguiente árbol:
46




Se quiere insertar el elemento 6.

Lo primero es comparar el nuevo elemento con la raíz. Como 6 > 4, entonces la
búsqueda prosigue por el lado derecho. Ahora el nuevo nodo se compara con el
elemento 8. En este caso 6 < 8, por lo que hay que continuar la búsqueda por la
rama izquierda. Como la rama izquierda de 8 no tiene ningún nodo, se cumple la
condición de parada de la recursividad y se inserta en ese lugar el nuevo nodo.




Borrar

El borrado en árboles binarios de búsqueda es otra operación bastante sencilla
excepto en un caso. Vamos a ir estudiando los distintos casos.

Tras realizar la búsqueda del nodo a eliminar observamos que el nodo no tiene
hijos. Este es el caso más sencillo, únicamente habrá que borrar el elemento y ya
habremos concuído la operación.

Si tras realizar la búsqueda nos encontramos con que tiene un sólo hijo. Este caso
también es sencillo, para borrar el nodo deseado, hacemos una especie de
puente, el padre del nodo a borrar pasa a apuntar al hijo del nodo borrado.
47




Por último, el caso más complejo, si el nodo a borrar tiene dos hijos. En este caso
se debe sustitui el nodo a borrar por mayor de los nomdos menores del nodo
borrado, o por el menor de los nodos mayores de dicho nodo. Una vez realizada
esta sustitución se borrra el nodo que sustituyó al nodo eliminado (operación
sencilla ya que este nodo tendrá un hijo a lo sumo).

Sobre el siguiente árbol queremos eliminar el elemento 6. Tenemos dos opciones
para sustituirlo:

       El menor de sus mayores: 7.
       El mayor de sus menores: 4.




Vamos a sustituirlo por el 7 (por ejemplo). El árbol resultante sería el siguiente,
tras eliminar también el elemento 7 de su ubicación original.
48




Otras operaciones

En los árboles de búsqueda la operación buscar es muy eficiente. El algoritmo
compara el elemento a buscar con la raíz, si es menor continua la búsqueda por la
rama izquierda, si es mayor continua por la izquierda. Este procedimiento se
realiza recursivamente hasta que se encuentra el nodo o hasta que se llega al final
del árbol.

Otra operación importante en el árbol es el recorridod el mismo. El recorrido se
puede realizar de tres formas diferentes:

      Preorden: Primero el nodo raíz, luego el subárbol izquierdo y a continuación
      el subárbol derecho.
      Inorden: Primero el subárbol izquierdo, luego la raíz y a continuación el
      subárbol derecho.
      Postorden: Primero el subárbol izquierdo, luego el subárbol derecho y a
      continuación la raíz.


                                   Aplicaciones:



Aplicaciones de árboles binarios

Un árbol binario es una estructura de datos útil cuando se trata de hacer modelos
de procesos en donde se requiere tomar decisiones en uno de dos sentidos en
cada parte del proceso. Por ejemplo, supongamos que tenemos un arreglo en
donde queremos encontrar todos los duplicados. Esta situación es bastante útil en
el manejo de las bases de datos, para evitar un problema que se llama
redundancia.
Una manera de encontrar los elementos duplicados en un arreglo es recorrer todo
                                                                                    49
el arreglo y comparar con cada uno de los elementos del arreglo. Esto implica que
si el arreglo tiene  elementos, se deben hacer    comparaciones, claro, no es
mucho problema si      es un número pequeño, pero el problema se va
complicando más a medida que       aumenta.

Si usamos un árbol binario, el número de comparaciones se reduce bastante,
veamos cómo.

El primer número del arreglo se coloca en la raíz del árbol (como en este ejemplo
siempre vamos a trabajar con árboles binarios, simplemente diremos árbol, para
referirnos a un árbol binario) con sus subárboles izquierdo y derecho vacíos.
Luego, cada elemento del arreglo se compara son la información del nodo raíz y
se crean los nuevos hijos con el siguiente criterio:

      Si el elemento del arreglo es igual que la información del nodo raíz,
      entonces notificar duplicidad.
      Si el elemento del arreglo es menor que la información del nodo raíz,
      entonces se crea un hijo izquierdo.
      Si el elemento del arreglo es mayor que la información del nodo raíz,
      entonces se crea un hijo derecho.

Una vez que ya está creado el árbol, se pueden buscar los elementos repetidos.
Si x el elemento buscado, se debe recorrer el árbol del siguiente modo:


Sea k la información del nodo actual p. Si        entonces cambiar el nodo actual
a right(p), en caso contrario, en caso de que        informar una ocurrencia

duplicada y en caso de que          cambiar el nodo actual a left(p).

El siguiente algoritmo

leer numero buscado >> n
tree=makeTree(n)
while(hay numeros en el arreglo){
  leeSiguienteNumero >> k
  p=q=tree;
  while(k!=info(p)&&q!=NULL){
    p=q
    if(k<info(p))
       q=left(p)
      else
       q=right(p)
  }
  if(k==info(p))
despliega<<" el numero es duplicado";
                                                                                      50
    else
      if (k<info(p))
        setLeft(p,k)
       else
        setRight(p,k)
}




             Figura 28: Árbol binario para encontrar números duplicados

Para saber el contenido de todos los nodos en un árbol es necesario recorrer el
árbol. Esto es debido a que solo tenemos conocimiento del contenido de la
dirección de un nodo a la vez. Al recorrer el árbol es necesario tener la dirección
de cada nodo, no necesariamente todos al mismo tiempo, de hecho normalmente
se tiene la dirección de uno o dos nodos a la vez; de manera que cuando se tiene
la dirección de un nodo, se dice que se visita ese nodo.

Aunque hay un orden preestablecido (la enumeración de los nodos) no siempre es
bueno recorrer el árbol en ese orden, porque el manejo de los apuntadores se
vuelve más complejo. En su lugar se han adoptado tres criterios principales para
recorrer un árbol binario, sin que de omita cualquier otro criterio diferente.

Los tres criterios principales para recorrer un árbol binario y visitar todos sus
nodos son, recorrer el árbol en:

preorden:
      Se ejecutan las operaciones:

            1. Visitar la raíz
            2. recorrer el subárbol izquierdo en preorden
            3. recorrer el subárbol derecho en preorden

entreorden:
      Se ejecutan las operaciones:

            1. recorrer el subárbol izquierdo en entreorden
            2. Visitar la raíz
3. recorrer el subárbol derecho en entreorden
                                                                                   51
postorden:
      Se ejecutan las operaciones:

          1. recorrer el subárbol izquierdo en postorden
          2. recorrer el subárbol derecho en postorden
          3. Visitar la raíz

Al considerar el árbol binario que se muestra en la figura 28 usando cada uno de
los tres criterios para recorrer el árbol se tienen las siguientes secuencias de
nodos:


En preorden:


En entreorden:


En postorden:

Esto nos lleva a pensar en otra aplicación, el ordenamiento de los elementos de
un arreglo.

Para ordenar los elementos de un arreglo en sentido ascendente, se debe
construir un árbol similar al árbol binario de búsqueda, pero sin omitir las
coincidencias.

El arreglo usado para crear el árbol binario de búsqueda fue

<14,15,4,9,7,18,3,5,16,4,20,17,9,14,5>

El árbol de ordenamiento es el que se muestra en la figura 29
Figura 29: Árbol binario para ordenar una secuencia de números             52




                             Arboles balanceados (avl)

Un árbol AVL (llamado así por las iniciales de sus inventores: Adelson-Velskii y
Landis) es un árbol binario de búsqueda en el que para cada nodo, las alturas de
sus subárboles izquierdo y derecho no difieren en más de 1.

No se trata de árboles perfectamente equilibrados, pero sí son lo suficientemente
equilibrados como para que su comportamiento sea lo bastante bueno como para
usarlos donde los ABB no garantizan tiempos de búsqueda óptimos.

El algoritmo para mantener un árbol AVL equilibrado se basa en reequilibrados
locales, de modo que no es necesario explorar todo el árbol después de cada
inserción o borrado.

Operaciones en AVL

Los AVL son también ABB, de modo que mantienen todas las operaciones que
poseen éstos. Las nuevas operaciones son las de equilibrar el árbol, pero eso se
hace como parte de las operaciones de insertado y borrado.

Factor de equilibrio

Cada nodo, además de la información que se pretende almacenar, debe tener los
dos punteros a los árboles derecho e izquierdo, igual que los ABB, y además un
miembro nuevo: el factor de equilibrio.

El factor de equilibrio es la diferencia entre las alturas del árbol derecho y el
izquierdo:

FE = altura subárbol derecho - altura subárbol izquierdo;

Por definición, para un árbol AVL, este valor debe ser -1, 0 ó 1.

Rotación simple a la derecha (SD)

Esta rotación se usará cuando el subárbol izquierdo de un nodo sea 2 unidades
más alto que el derecho, es decir, cuando su FE sea de -2. Y además, la raíz del
subárbol izquierdo tenga una FE de -1 ó 0, es decir, que esté cargado a la
izquierda o equilibrado.
Grafos:
                                                                                         53

Grafos

Desafortunadamente no existe una terminología estandarizada en la teoría de los
grafos, por lo tanto es oportuno aclarar que las presentes definiciones pueden
variar ligeramente entre diferentes publicaciones de estructura de datos y de teoría
de grafos, pero en general se puede decir que un grafo como indica su nombre lo
indica es la representación (para nuestro caso) gráfica de los datos de una
situación particular, ejemplo:

   Los datos contienen, en algunos casos, relaciones entre ellos que no es
necesariamente jerárquica. Por ejemplo, supongamos que unas líneas aéreas
realizan vuelos entre las ciudades conectadas por líneas como se ve en la figura
anterior (más adelante se presentaran grafos con estructuras de datos); la
estructura de datos que refleja esta relación recibe el nombre de grafo.

Se suelen usar muchos nombres al referirnos a los elementos de una estructura
de datos. Algunos de ellos son "elemento", "ítem", "asociación de ítems",
"registro", "nodo" y "objeto". El nombre que se utiliza depende del tipo de
estructura, el contexto en que usamos esa estructura y quien la utiliza.

En la mayoría de los textos de estructura de datos se utiliza el termino "registro" al
hacer referencia a archivos y "nodo" cuando se usan listas enlazadas, árboles y
grafos.

También un grafo es una terna G = (V,A,j ), en donde V y A son conjuntos finitos, y
j es una aplicación que hace corresponder a cada elemento de A un par de
elementos de V. Los elementos de V y de A se llaman, respectivamente, "vértices"
y "aristas" de G, y j asocia entonces a cada arista con sus dos vértices.

Esta definición da lugar a una representación gráfica, en donde cada vértice es un
punto del plano, y cada arista es una línea que une a sus dos vértices.

Aristas

Son las líneas con las que se unen las aristas de un grafo y con la que se
construyen también caminos.

Si la arista carece de dirección se denota indistintamente {a, b} o {b, a}, siendo a y
b los vértices que une.

Si {a ,b} es una arista, a los vértices a y b se les llama sus extremos.

       Aristas Adyacentes: Se dice que dos aristas son adyacentes si convergen
       en el mismo vértice.
54
      Aristas Paralelas: Se dice que dos aristas son paralelas si vértice inicial y el
      final son el mismo.


      Aristas Cíclicas: Arista que parte de un vértice para entrar en el mismo.


      Cruce: Son dos aristas que cruzan en un punto.


Vértices

Son los puntos o nodos con los que esta conformado un grafo. Llamaremos grado
de un vértice al número de aristas de las que es extremo. Se dice que un vértice
es `par' o `impar' según lo sea su grado.

      Vértices Adyacentes: si tenemos un par de vértices de un grafo (U, V) y si
      tenemos un arista que los une, entonces U y V son vértices adyacentes y
      se dice que U es el vértice inicial y V el vértice adyacente.
      Vértice Aislado: Es un vértice de grado cero.


      Vértice Terminal: Es un vértice de grado 1.


Caminos

Sean x, y " V, se dice que hay un camino en G de x a y si existe una sucesión
finita no vacía de aristas {x,v1}, {v1,v2},..., {vn,y}. En este caso

      x e y se llaman los extremos del camino


      El número de aristas del camino se llama la longitud del camino.


      Si los vértices no se repiten el camino se dice propio o simple.


      Si hay un camino no simple entre 2 vértices, también habrá un camino
      simple entre ellos.


      Cuando los dos extremos de un camino son iguales, el camino se llama
      circuito o camino cerrado.
Llamaremos ciclo a un circuito simple
                                                                                        55

       Un vértice a se dice accesible desde el vértice b si existe un camino entre
       ellos. Todo vértice es accesible respecto a si mismo




                             Terminología delos grafos



Un grafo está formado por un conjunto de nodos(o vértices) y un conjunto de
arcos. Cada arco en un grafo se especifica por un par de nodos.
El conjunto de nodos es {A, B, C, D, F, G, H} y el conjunto de arcos {(A, B), (A, D),
(A,C), (C, D), (C, F), (E, G), (A, A)} para el siguiente grafo




                        Operaciones básicas sobre grafos

Suponga que un grafo G se mantiene en memoria mediante la representación
enlazada
GRAFO (NODO, SIG, ADY, PRINCIPIO, NDISP, ENL, ADISP)
Discutida en la sección anterior. Esta sección discute las operaciones, de
búsqueda inserción y eliminación de nodos y aristas en el grafo G. La operación
de recorrido se trata en la siguiente sección.




Procedimiento


Busca(INFO, ENL, PRINCIPIO, ITEM, POS) [Algoritmo 5.2]
Busca la posición POS del primer nodo que contiene a ITEM, o hace
                                                                                       56
POS : = NULO.
1.-Hacer PTR : =PRINCIPIO.
2.-Repetir mientras PTR sea diferente de NULO:
Si ITEM = INFO[PTR], entonces; Hacer POS := PTR y
Volver.
Si no hacer PTR : = ENL [PTR].
[Fin del Bucle].
3.-Hacer POS : = NULO y volver.


Diferente que en el capítulo 5. Por supuesto, si se usan listas enlazadas circulares
a árboles binarios de búsqueda en vez de listas enlazadas, se deben de usar los
procedimientos análogos.
BÚSQUEDA EN UN GRAFO
Suponga que queremos encontrar la posición POS de un nodo N de un grafo C .
Esto se
puede llevar a cabo mediante el Procedimiento 8.3, tal como sigue:
Llamar BUSCA (NODO, SIG, PRINCIPIO, N, POS)
O sea, que esta sentencia de llamada busca en la lista NODO el nodo N.
Procedimiento 8.4 ELIMINAR (INFO, ENL, PRINCIPIO, DISP, ITEM, INDIC)
[Algoritmo 5.10]
Elimina el primer nodo de la lista que contenga a ITEM, o hace
INDIC:=FALSO si ITEM no aparece en la lista.
1.-[¿Lista vacia?] Si PRINCIPIO = NULO, entonces: Hacer
INDIC : = FALSO y volver .
2.-[¿ITEM en primer nodo?] Si INFO [PRINCIPIO] = ITEM,
Entonces: Hacer PTR : = PRINCIPIO,
PRINCIPIO: =ENL[PRINCIPIO],
ENL[PTR]: = DISP, DISP : = PTR,
INDIC : = VERDADERO y volver.
[Fin del condicional].
3.-Hacer PTR : = ENL[PRINCIPIO] y SALVA : = PRINCIPIO.
                                                                             57
[Inicializar punteros].

4.-Repetir pasos 5 y 6 mientras PTR sea diferente a NULO:

5.- Si INFO [PTR] = ITEM entonces:
Hacer ENL [SALVA]: = ENL[PTR],
ENL[PTR]: = DISP, DISP: = PTR,
INDIC: =VERDADERO y volver.
[Fin de la condicional].
6.- Hacer SALVA := PTR y PTR := ENL [PTR].
[Actualizar punteros].
[Fin Bucle del paso 4].
7.- Hacer INDIC : = FALSO y Volver.

INSERCIÓN EN UN GRAFO
Suponga que se va a insertar un nodo N en el grafo G. Observe que N será
asignado a
NODO [NDISP], el primer nodo disponible. Mas aún, como N será un nodo
aislado, se debe hacer ADY[NDISP]: = NULO. El procedimiento 8.6 hace esto
mediante una variable lógica INDIC que indica si hay desbordamiento.
Por supuesto, el Procedimiento 8.6 debe ser modificado si la lista NODO se
mantiene como una lista ordenada o como un árbol binario de búsqueda.
Procedimiento
 INSNODO(NODO, SIG, ADY, PRINCIPIO, NDISP, N, INDIC)
Este procedimiento inserta el nodo N en le grafo G.
1.-[¿DESBORDAMIENTO?] Si NDISP= NULO, entonces: Hacer
INDIC := FALSO y volver.
2.- Hacer ADY[NDISP] : = NULO.
3.-[Quitar el nodo de la lista NDISP].
Hacer NUEVO: =NDISP y NDISP :=SIG[NDISP].
4.-[Insertar nodo N en la lista NODO].
Hacer NODO[NUEVO] : = N, SIG[NUEVO] :=PRINCIPIO y
PRINCIPIO : = NUEVO.
                                                                                     58
5.-Hacer INDIC := VERDADERO y Volver.

ELIMINACIÓN DE UN ARCO EN UN GRAFO
Suponga que se va a eliminar una arista (A, B) del grafo G. (Nuestro
procedimiento asumirá que A y B son nodos de G). De nuevo debemos encontrar
primero la posición
POSA de A y la posición POSB de B en la lista de nodos. Entonces simplemente
eliminamos POSB de la lista de sucesores de A, que tiene el puntero de la lista
ADY[POSA].Se usa una variable INDIC lógica para indicar si no existe tal arista en
el grafo G. El procedimiento es el siguiente:
Procedimiento 8.8 ELIMARISTA(NODO, SIG, ADY, PRINCIPIO, DEST, ENL,
ADISP, A, B, INDIC).
Este procedimiento elimina la arista (A, B) del grafo G .
1.-Llamar BUSCA(NODO, SIG, PRINCIPIO, A, POSA)[Localizar
Nodo A].
2.- Llamar BUSCA(NODO, SIG, PRINCIPIO, B, POSB) [Localizar
Nodo B].
3.-Llamar ELIMINAR (DEST, ENL, ADY[POSA], ADISP, POSB,
INDIC).[Usar el procedimiento 8.4].
4.-Volver.
BIBLIOGRAFIA
                                                                               59

Técnicas de Diseño de Algoritmos en Java-Escrito por Sonia Jaramillo
Valbuena,Sonia Jaramillo Valbuena, Sergio Augusto Cardona Torres, Maria Lili
Villegas Ramirez Página 162


Estructuras de datos y algoritmos con Java-Escrito por Adam Drozdek Página
261


Introducción a las Estructuras de datos en Java- Escrito por Sonia Jaramillo
Valbuena,Sonia Jaramillo Valbuena, Sergio Augusto Cardona Torres, Dumar
Antonio Villa Zapata Página 209
Hacer un programa que manipule una lista o una pila o una cola   60
          pero que se vea la animación de la operación realizada



package Clases;




import java.awt.*;

import javax.swing.JPanel;



public class PanelDibujo extends JPanel {



public int x=20;

public int y=100;

public pila Pila;

public boolean swborrar=false;



public PanelDibujo(pila p){

Pila=p;

}



public void paintComponent(Graphics g){

super.paintComponents(g);

Graphics2D g2d=(Graphics2D)g;

g2d.setColor(Color.WHITE);

g2d.fillRect(0,0,400,200);
g2d.setColor(Color.BLACK);
                                                                 61
g2d.setStroke(new BasicStroke(2));

for(int i=0;i<Pila.getNodos().size();i++){

if(i!=0){

g2d.drawLine(2*x*(i+1)-10, y+15, 2*x*(i+1), y+15);

}

g2d.drawRoundRect(2*x*(i+1), y, 30, 30, 10, 10);

g2d.drawString(""+Pila.getNodos().get(i), 2*x*(i+1)+12, y+20);

}



if(swborrar){

g2d.setColor(Color.WHITE);

g2d.fillRect(0,0,400,200);

Pila.resetear(true);

swborrar=false;

}

}




public boolean isSwborrar() {

return swborrar;

}




public void setSwborrar(boolean swborrar) {
this.swborrar = swborrar;
                                           62
}

public static void main(String[] args) {



       }

}
MÉTODOS DE ORDENAMIENTO
                                                                      63



Ordenamiento por burbuja

package BurbujaPaq;
public class Burbujamilenteros {
   public static void main(String[] args) {
           int x[]= new int [100];
           int i,j, aux;
           System.out.println("---------NUMEROS GENERADOS-------");
           for (i=0; i<x.length; i++) {
                      x[i]=(int)(Math.random()*1000);
                      System.out.println(x[i] + " | ");
           }
           System.out.println("");
           System.out.println("---------NUMEROS ORDENADOS-------");
           for (i=0; i<x.length; i++) {
                      for (j=0; j<x.length-1; j++) {
                                 if (x[j] > x[j+1]){
                                      aux=x[j];
                       x[j]=x[j+1];
                       x[j+1]=aux;
                          }
                      }
           }
for (i=0; i<x.length; i++) {
           System.out.println(x[i] + " | ");
      }
   }
}

Hacer un programa en Java que muestre el siguiente menú:

1. Burbuja

2. Shell

3. Quicksort

4. Radix
Para cada opción se ordenaran primero 500 elementos y se le                             64
toma el tiempo, luego 1000 y se le toma el tiempo y por último
2000 elementos y se le toma el tiempo. Al final imprimir los
arreglos originales y su tiempo de ordenamiento.



/ Crea un archivo de acceso aleatorio, escribiendo 100 registros vacíos en el disco.

import java.io.*;

import javax.swing.*;



public class CrearArchivoAleatorio {



  private static final int NUMERO_REGISTROS = 100;



  // permitir al usuario seleccionar el archivo a abrir

  private void crearArchivo()

  {

      // mostrar cuadro de diálogo para que el usuario pueda seleccionar el archivo

      JFileChooser selectorArchivo = new JFileChooser();

      selectorArchivo.setFileSelectionMode( JFileChooser.FILES_ONLY );



      int resultado = selectorArchivo.showSaveDialog( null );



      // si el usuario hizo clic en el botón Cancelar del cuadro de diálogo, regresar

      if ( resultado == JFileChooser.CANCEL_OPTION )

       return;
// obtener el archivo seleccionado
                                                                         65
File nombreArchivo = selectorArchivo.getSelectedFile();



// mostrar error si el nombre del archivo es inválido

if ( nombreArchivo == null || nombreArchivo.getName().equals( "" ) )

  JOptionPane.showMessageDialog( null, "Nombre de archivo incorrecto",

    "Nombre de archivo incorrecto", JOptionPane.ERROR_MESSAGE );



else {



  // abrir el archivo

  try {

    RandomAccessFile archivo =

         new RandomAccessFile( nombreArchivo, "rw" );



    RegistroCuentasAccesoAleatorio registroEnBlanco =

         new RegistroCuentasAccesoAleatorio();



    // escribir 100 registros en blanco

    for ( int cuenta = 0; cuenta < NUMERO_REGISTROS; cuenta++ )

         registroEnBlanco.escribir( archivo );



    archivo.close(); // cerrar el archivo



    // mostrar mensaje indicando que el archivo se creó
JOptionPane.showMessageDialog( null, "Se creó el archivo " +
                                                                                    66
            nombreArchivo, "Estado", JOptionPane.INFORMATION_MESSAGE );



           System.exit( 0 ); // terminar el programa



       } // fin del bloque try



      // procesar excepciones durante operaciones de apertura, escritura o cierre
del archivo

       catch ( IOException excepcionES ) {

           JOptionPane.showMessageDialog( null, "Error al procesar el archivo",

            "Error al procesar el archivo", JOptionPane.ERROR_MESSAGE );



           System.exit( 1 );

       }



     } // fin de instrucción else



 } // fin del método crearArchivo



 public static void main( String args[] )

 {

     CrearArchivoAleatorio aplicacion = new CrearArchivoAleatorio();

     aplicacion.crearArchivo();

 }
67
} // fin de la clase CrearArchivoAleatorio
MÉTODOS DE BÚSQUEDA                                                    68




Programa de los hombres búsqueda binaria

import java.awt.*;

import java.awt.event.*;

import java.io.*;

import javax.swing.*;




public class PruebaFile extends JFrame

 implements ActionListener {




 private JTextField campoEntrada;

 private JTextArea areaSalida;




 public PruebaFile()

 {

     super( "Prueba de la clase File" );




     campoEntrada = new JTextField( "Escriba aquí el nombre del archivo o directorio" );
campoEntrada.addActionListener( this );
                                                                 69
    areaSalida = new JTextArea();




    ScrollPane panelDesplazable = new ScrollPane();

    panelDesplazable.add( areaSalida );




    Container contenedor = getContentPane();

    contenedor.add( campoEntrada, BorderLayout.NORTH );

    contenedor.add( panelDesplazable, BorderLayout.CENTER );




    setSize( 400, 400 );

    setVisible( true );




}

public void actionPerformed( ActionEvent eventoAccion )

{

    File nombre = new File( eventoAccion.getActionCommand() );
if ( nombre.exists() ) {
                                                                      70
 areaSalida.setText( nombre.getName() + " existen" +

   ( nombre.isFile() ? "es un archivon" : "no es un archivon" ) +

   ( nombre.isDirectory() ? "es un directorion" :

     "no es un directorion" ) +

   ( nombre.isAbsolute() ? "es una ruta absolutan" :

     "no es una ruta absolutan" ) + "Última modificación: " +

   nombre.lastModified() + "nLongitud: " + nombre.length() +

   "nRuta: " + nombre.getPath() + "nRuta absoluta: " +

   nombre.getAbsolutePath() + "nPadre: " + nombre.getParent() );




 if ( nombre.isFile() ) {




   try {

     BufferedReader entrada = new BufferedReader(

       new FileReader( nombre ) );

     StringBuffer bufer = new StringBuffer();

     String texto;

     areaSalida.append( "nn" );



     while ( ( texto = entrada.readLine() ) != null )
bufer.append( texto + "n" );
                                                                   71



        areaSalida.append( bufer.toString() );

    }




    catch( IOException excepcionES ) {

        JOptionPane.showMessageDialog( this, "ERROR EN ARCHIVO",

         "ERROR EN ARCHIVO", JOptionPane.ERROR_MESSAGE );

    }




}

else if ( nombre.isDirectory() ) {

    String directorio[] = nombre.list();



    areaSalida.append( "nnContenido del directorio:n");



    for ( int i = 0; i < directorio.length; i++ )

        areaSalida.append( directorio[ i ] + "n" );

}
72
        }

        else {

            JOptionPane.showMessageDialog( this,

             eventoAccion.getActionCommand() + " no existe",

             "ERROR", JOptionPane.ERROR_MESSAGE );

        }




    }




    public static void main( String args[] )

    {

        PruebaFile aplicacion = new PruebaFile();

        aplicacion.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

    }




}



Programa de las mujeres búsqueda secuencial

import java.util.*;
73



class programa8

{

public static void main(String[]args)

{

int a[],n,n1,indice;



Scanner sc=new Scanner(System.in);



System.out.print("Ingresa tamaño del vector: ");

n=sc.nextInt();



a=new int[n];



a=inicializa(n);

muestra(a);



System.out.print("Ingresa numero a buscar: ");

n1=sc.nextInt();



indice=busquedaLineal(a,n1);

if(indice==-1)
{
                                                              74
System.out.println("tu número no esta en la lista");

}

else

{

System.out.println("tu número esta en el indice: "+indice);

}

}



static int[] inicializa(int n)

{

int i,j,a[]=new int[n];

for(i=0;i<n;i++)

{

a[i]=randomxy(1,50);

}



return a;

}



static int busquedaLineal(int a[],int n)

{

int i;
for (i=0;i<a.length;i++)
                                   75
{

if (a[i] == n)

{

return i+1;

}

}

return -1;

}



static void muestra(int a[])

{

int n=a.length;

for(int i=0;i<n;i++)

{

System.out.print(a[i]+" ");

}

System.out.print("nn");



}



static int randomxy(int x,int y)

{
int ran=(int) (Math.floor(Math.random()*(y-x+1))+x);
                                                                                76



return ran;

}

}




Programa De Examen



import java.util.*;



class busqueda

{

                       public static void main(String[]args)

                       {

                            int a[],n,n1,indice;



                            Scanner sc=new Scanner(System.in);



                            System.out.print("Ingresa tamaño del arreglo: ");

                            n=sc.nextInt();



                            a=new int[n];



                            a=inicializa(n);
muestra(a);
                                                                               77



                         System.out.print("Ingresa numero a buscar: ");

                         n1=sc.nextInt();



                         indice=busqueda1(a,n1);

                         if(indice==-1)

                         {

                                 System.out.println("tu número no esta en la
lista");

                         }

                         else

                         {

                                 System.out.println("tu número esta en el
indice: "+indice);

                         }

}



                     static int[] inicializa(int n)

                     {

                         int i,j,a[]=new int[n];

                         for(i=0;i<n;i++)

                         {

                                 a[i]=randomxy(1,50);

                         }
return a;
                                           78
}



static int busqueda1(int a[],int n)

{

    int i;

    for (i=0;i<a.length;i++)

    {

             if (a[i] == n)

             {

                    return i+1;

             }

    }

    return -1;

}



static void muestra(int a[])

{

    int n=a.length;

    for(int i=0;i<n;i++)

    {

             System.out.print(a[i]+" ");

    }

    System.out.print("nn");
}
                                                                    79



         static int randomxy(int x,int y)

         {

             int ran=(int) (Math.floor(Math.random()*(y-x+1))+x);



             return ran;

         }

}




    ANÁLISIS DE LOS ALGORITMOS
80


                                    INTRODUCCIÓN
Los algoritmos se pueden clasificarse según la complejidades de tiempo y espacio, y a
este respecto pueden distinguirse varias clases de algoritmos, como lo ilustran la
siguiente figura. Su crecimiento también se muestra, por ejemplo, los algoritmo se llama
constante si su tiempo de ejecución sigue siendo el mismo para cualquier número de
elementos; se llama cuadrático si su tiempo de ejecución es O (n2).para cada una de
estas clases de algoritmos y sus tiempo de ejecución en una computadora que realiza
un millón de operaciones por segundo (1s=106µs=103ms).


         Clase     complejidad          10                  102              103
            n
     constante     O(1)          1           1µs     1        1µs     1        1µs
     Logarítmica   O(1gn)        3.32        3µs     6.64     7µs     9.97     10µs
     Lineal        O(n)          10          10µs    102      100µs   103      1ms
     O(n1gn)       O(n1gn)       33.2        33µs    664      664µs   9970     10ms
     Cuadrática    O(n2)         102         100µs   104      10ms    106      1s




       ANÁLISIS DE LOS ALGORITMOS.
81
Es una disciplina de las ciencias de la computación y, en la mayoría de los casos, su
estudio   es   completamente     abstracto   sin   usar   ningún   tipo   de   lenguaje   de
programación ni cualquier otra implementación. Así, el análisis de los algoritmos se
centra en los principios básicos del algoritmo, no en los de la implementación particular.
Una forma de plasmar (o algunas veces "codificar") un algoritmo es escribirlo
en pseudocódigo o utilizar un lenguaje muy simple tal como Léxico, cuyos códigos
pueden estar en el idioma del programador.

Por ejemplo:

Un algoritmo que verifica que hay más ceros que unos en una secuencia binaria infinita
debe ejecutarse siempre para que pueda devolver un valor útil. Si se implementa
correctamente, el valor devuelto por el algoritmo será válido, hasta que evalúe el
siguiente dígito binario. De esta forma, mientras evalúa la siguiente secuencia podrán
leerse dos tipos de señales: una señal positiva (en el caso de que el número de ceros
sea mayor que el de unos) y una negativa en caso contrario. Finalmente, la salida de
este algoritmo se define como la devolución de valores exclusivamente positivos si hay
más ceros que unos en la secuencia y, en cualquier otro caso, devolverá una mezcla de
señales positivas y negativas.




   COMPLEJIDAD EN EL TIEMPO Y ESPACIO
82



Complejidad en el tiempo:Cantidad de tiempo necesario para la ejecución




Órdenes de Complejidad

Se dice que O (f(n)) define un "orden de complejidad". Escogeremos como
representante de este orden a la función f(n) más sencilla del mismo. Así tendremos
O(1) orden constante
O(log n)
      orden logarítmico
O(n) orden lineal
O(n log n)
O(n2) orden cuadrático
O(na) orden polinomiol (a > 2)
O(an) orden exponencial (a > 2)
O (n!) orden factorial

Es más, se puede identificar una jerarquía de órdenes de complejidad que coincide con
el orden de la tabla anterior; jerarquía en el sentido de que cada orden de complejidad
superior tiene a los inferiores como subconjuntos. Si un algoritmo A se puede demostrar
de un cierto orden O1, es cierto que también pertenece a todos los órdenes superiores
(la relación de orden cota superior de' es transitiva); pero en la práctica lo útil es
encontrar la "menor cota superior", es decir el menor orden de complejidad que lo
cubra. Los algoritmos de complejidad O(n) y O(n log n) son los que muestran un
comportamiento más "natural": prácticamente a doble de tiempo, doble de datos procesables.
Los algoritmos de complejidad logarítmica son un descubrimiento fenomenal, pues en el doble
de tiempo permiten atacar problemas notablemente mayores, y para resolver un problema el
doble de grande sólo hace falta un poco más de tiempo (ni mucho menos el doble).
Los algoritmos de tipo polinómico no son una maravilla, y se enfrentan con dificultad a
                                                                                          83
problemas de tamaño creciente. La práctica viene a decirnos que son el límite de lo
"tratable".

Sobre la tratabilidad de los algoritmos de complejidad polinómicahabria mucho que
hablar, y a veces semejante calificativo es puro eufemismo. Mientras complejidades del
orden O (n2) y O (n3) suelen ser efectivamente abordables, prácticamente nadie acepta
algoritmos de orden O(n100), por muy polinómicos que sean. La frontera es imprecisa.

Cualquier algoritmo por encima de una complejidad polinómico se dice "intratable" y
sólo será aplicable a problemas ridículamente pequeños.

Por ejemplo, si disponemos de dos algoritmos para el mismo problema, con tiempos de
ejecución respectivos:

      algoritmo tiempo complejidad
      f          100 n     O(n)
                     2
      g          n         O(n2)

Asintóticamente, "f" es mejor algoritmo que "g"; pero esto es cierto a partir de N >
100.Si nuestro problema no va a tratar jamás problemas de tamaño mayor que 100,

es mejor solución usar el algoritmo "g". El ejemplo anterior muestra que las

constantes que aparecen en las fórmulas para T(n), y que desaparecen al calcularlas
funciones de complejidad, pueden ser decisivas desde el punto de vista de ingeniería.
Pueden darse incluso ejemplo:

      algoritmo tiempo complejidad
      f          100 n     O(n)
      g          n2        O(n2)
84

Las siguientes propiedades se pueden utilizar como reglas para el cálculo de órdenes
de complejidad. Toda la maquinaria matemática para el cálculo de límites se puede
aplicar directamente:

C. Lim (n->inf) f (n)/g (n) = 0 => f IN O (g
) =>g NOT_IN O(f)=>O(f) es subconjunto de O(g)
D. Lim (n->inf) f (n)/g (n) = k => f IN O(g)
                                               =>g IN O(f)
                                               => O(f) = O(g)
E. Lim(n->inf)f(n)/g(n)= INF => f NOT_IN O(g)
                                               =>g IN O(f)
                                               =>O(f) es superconjunto de O(g)

Las que siguen son reglas habituales en el cálculo de límites:

F. Si f, g IN O(h) =>f+g IN O(h)
G. Sea k una constante, f(n) IN O(g) => k*f(n) IN O(g)
H. Si f IN O(h1) y g IN O(h2) =>f+g IN O(h1+h2)
I. Si f IN O(h1) y g IN O(h2) => f*g IN O(h1*h2)
J. Sean los reales 0 < a < b =>O(na) es subconjunto de O(nb)
K. Sea P(n) un polinomio de grado k => P(n) IN O(nk)
L. Sean los reales a, b > 1 =>O(loga) = O(logb)

La regla [L] nos permite olvidar la base en la que se calculan los logaritmos en
expresiones de complejidad. La combinación de las reglas [K, G] es probablemente la
más usada, permitiendo de un plumazo olvidar todos los componentes de un polinomio,
menos su grado.

Por último, la regla [H] es la basica para analizar el concepto de secuencia en un
programa: la composición secuencial de dos trozos de programa es de orden de
complejidad el de la suma de sus partes.
Costes en tiempo y en espacio
                                                                                           85
La característica básica que debe tener un algoritmo es que sea correcto, es decir, que
produzca el resultado deseado en tiempo finito. Adicionalmente puede interesarnos que
sea claro, que este bien estructurado, que sea fácil de usar, que sea fácil de
implementar y que sea eficiente.
Entendemos por eficiencia de un algoritmo la cantidad de recursos de computo que
requiere; es decir, cual es su tiempo de ejecución que cantidad de memoria utiliza.
A la cantidad de tiempo que requiere la ejecución de un cierto algoritmo se le suele
llamar coste en tiempo mientras que a la cantidad de memoria que requiere se le suele
llamar coste en espacio.
Es evidente que conviene buscar algoritmos correctos que mantengan tan bajo como
sea posible el consumo de recursos que hacen del sistema, es decir, que sean lo mas
eficientes posible. Cabe hacer notar que el concepto de eficiencia de un algoritmo es un
concepto relativo, esto quiere decir que ante dos algoritmos correctos que resuelven el
mismo problema, uno es mas eficiente que otro si consume menos recursos. Por tanto,
podemos observar que el concepto de eficiencia y en consecuencia el concepto de
coste nos permitirá comparar distintos algoritmos entre ellos.
Si consideramos los algoritmos elementales de ordenación por selección y por Inserción
¿Cómo podríamos elegir cual de ellos utilizar en una aplicación dada?}Veremos más
adelante que para efectos prácticos ambos algoritmos son similares y que son
eficientes solo para ordenar secuencias con un número pequeño de elementos. Sin
embargo hay varias comparaciones que se pueden hacer entre ellos.
Tamaño del vector Núm. Comparaciones Núm. Intercambios
Selección n(n − 1)/2 0 (min.)
n− 1 (max.)
n− 1 (min.) 0 (min.)
Insertion prop.n2/4 (prom.) prop. N2/4 (prom.)
Prop. N2/2 (max.) prop. N2/2
De la tabla anterior podemos inferir que para ordenar secuencias cuyos elementos
Sean difíciles de comparar (las comparaciones son caras) es más conveniente Utilizar
el método de ordenación por inserción, y que este método es más eficiente mientras
más ordenada este la secuencia de entrada. Si por el contrario queremos ordenar
                                                                                          86
secuencias de elementos que son fáciles de comparar (o que tienen claves asociadas
fáciles de comparar) y en cambio los intercambios son carosseria más conveniente
utilizar el algoritmo de ordenación por selección.
En general, ¿cómo podemos elegir entre un algoritmo y otro si ambos resuelven el
mismo problema?
Pero este método tiene varios inconvenientes ya que los resultados dependen: del
subconjunto de pruebas escogidas del ordenador en el que se realicen las pruebas del
lenguaje de programación utilizado o del compilador, entre otros factores.
Ejemplo. Análisis de la funciona os min, que encuentra la posición del elemento mínimo
en un vector de enteros.
El coste en tiempo de un algoritmo depende del tipo de operaciones que realiza y del
coste especifico de estas operaciones. Este coste suele calcularse únicamente en
función del tamaño de las entradas para que sea independiente del tipo de maquina,
lenguaje de programación, compilador, etc. en que se implementa el algoritmo.
Definición 1.1 Dado un algoritmo A cuyo conjunto de entradas es A, su eficiencia o
coste (en tiempo, en espacio, en número de operaciones de entrada/ salida, etc.) es
una función tal que:
T: A! R+
∞ 7! T (∞)

COMPARACION DE COMPLEJIDAD Y TIEMPO EN ALGORITMOS.

Se analizara el tiempo de ejecución y el tiempo empleado por los algoritmos de
inserción directa, selección directa, el método de la burbuja y el método de la burbuja
mejorado, ordenando una arreglo de enteros en 3 casos: en orden ascendente (mejor
caso), un orden descendente (peor caso) y un orden al azar (caso promedio).

ALGORITMOS:

- INSERCION DIRECTA:

Para x=2 hasta n (+)
Y x-1
                           87

Sw 0

Mientras (y<>0) y (sw=0)

Si A[y-1]>A[y]

AuxA[y-1]

A[y-1]A[y]

A[y]Aux

De lo contrario

Sw1

Yy-1

SELECCIÓN DIRECTA:

Para x=1 hasta n-1 (+)

Menorx

Para y=x+1 hasta n (+)

Si A[y] <A [menor]

Menory

Si menor<>x

AuxA[x]

A[x] A[menor]
A[menor] Aux
                                 88

- METODO DE LA BURBUJA:

Para i=1 hasta n-1

Para j=i+1 hasta n

Si A[i]>A[j]

AuxA[i]

A[i]A[j]

A[j]Aux

METODO DE LA BURBUJA MEJORADO:

Sw1

Mientras sw=1

Sw 0

Para i=1 hasta n-1 (+)

Si A[i] >A[i+1]

AuxA[i]

A[i]A[i+1]

A[i+1]Aux
COMPARACION DE COMPLEJIDADES:
                                                                                        89


      ORDEN DE COMPLEJIDAD
                       INSERCION       SELECCION       BURBUJA       BURBUJA
                       DIRECTA         DIRECTA                       (MEJORADO)
      MEJOR            O(N)            O(N2)           O(N2)         O(N)
      CASO
      CASO             O(N2)           O(N2)           O(N2)         O(N2)
      PROMEDIO
      PEOR CASO        O(N2)           O(N2)           O(N2)         O(N log N)



TIEMPO EN COMPARACIONES Y ASIGNACIONES:

Probar que son equivalentes las condiciones siguientes:

1. G es un grafo bipartito.
2. G es un grafo 2{coloreable.

3. G es un grafo que carece de ciclos de longitud impar.

1.   COMPLEJIDAD ALGORÍTMICA

Un algoritmo será mas eficiente comparado con otro, siempre que consuma menos
recursos, como el tiempo y espacio de memoria necesarios para ejecutarlo.

La eficiencia de un algoritmo puede ser cuantificada con las siguientes medidas de
complejidad:

1.   Complejidad Temporal o Tiempo de ejecución: Tiempo de cómputo necesario
para ejecutar algún programa.
2.   Complejidad Espacial: Memoria que utiliza un programa para su ejecución, La
eficiencia en memoria de un algoritmo indica la cantidad de espacio requerido para
ejecutar el algoritmo; es decir, el espacio en memoria que ocupan todas las variables
propias al algoritmo. Para calcular la memoria estática de un algoritmo se suma la
                                                                                           90
memoria que ocupan las variables declaradas en el algoritmo. Para el caso de la
memoria dinámica, el cálculo no es tan simple ya que, este depende de cada ejecución
del algoritmo. Este análisis se basa en las Complejidades Temporales, con este fin,
para cada problema determinaremos una medida N, que llamaremos tamaño de la
entrada o número de datos a procesar por el programa, intentaremos hallar respuestas
en función de dicha N.El concepto exacto que cuantifica N dependerá de la naturaleza
del problema, si hablamos de un array se puede ver a N como el rango del array, para
una matriz, el número de elementos que la componen; para un grafo, podría ser el
número de nodos o arcos que lo arman, no se puede establecer una regla para N, pues
cada problema acarrea su propia lógica y complejidad.

Tiempo de Ejecución: El tiempo de Ejecución de un programa se mide en función de
N, lo que designaremos como (N).Esta función se puede calcular físicamente
ejecutando el programa acompañados de un reloj, o calcularse directamente sobre el
código, contando las instrucciones a ser ejecutadas y multiplicando por el tiempo
requerido por cada instrucción. Así, un trozo sencillo de código como:

S1;

for(x = 0; x < N; x++)

S2;

Demanda: T(N) = t1 + t2 * N

Donde t1 es el tiempo que lleva ejecutar la serie S1 de sentencias, y t2 es el que lleva
la serie S2.Habitualmente todos los algoritmos contienen alguna sentencia condicional
o selectiva, haciendo que las sentencias ejecutadas dependan de la condición lógica,
esto hace que aparezca más de un valor para T(N), es por ello que debemos hablar de
un rango de valores:

Tmin(N) ≤ T(N) ≤ Tmax(N)
Estos extremos son llamados "el peor caso" y "el mejor caso" y entre ambos se puede
                                                                                             91
hallar "el caso promedio" o el más frecuente, siendo este el más difícil de estudiar; nos
centraremos en el "el peor caso" por ser de fácil cálculo y se acerca a "el caso
promedio", brindándonos una medida pesimista pero fiable.Toda función T(N) encierra
referencias al parámetro N, y a una serie de constantes Ti dependientes de factores
externos al algoritmo. Se tratará de analizar los algoritmos dándoles autonomía frente a
estos factores externos, buscando estimaciones generales ampliamente válidas, a
pesar de ser demostraciones teóricas.

COMPLEJIDAD EN ESPACIO
Cantidad de memoria necesaria para la ejecución
La misma idea que se utiliza para medir la complejidad en tiempo de un algoritmo se
utiliza para medir su complejidad en espacio. Decir que un programa es O( N ) en
espacio significa que sus requerimientos de memoria aumentan proporcionalmente con
el tamaño del problema. Esto es, si el problema se duplica, se necesita el doble de
memoria. Del mismo modo, para un programa de complejidad O( N2 ) en espacio, la
cantidad de memoria que se necesita para almacenar los datos crece con el cuadrado
del tamaño del problema: si el problema se duplica, se requiere cuatro veces más
memoria. En general, el cálculo de la complejidad en espacio de un algoritmo es un
proceso sencillo que se realiza mediante el estudio de las estructuras de datos y su
relación con el tamaño del problema.

El problema de eficiencia de un programa se puede plantear como un compromiso
entre el tiempo y el espacio utilizados. En general, al aumentar el espacio utilizado para
almacenar la información, se puede conseguir un mejor desempeño, y, entre más
compactas sean las estructuras de datos, menos veloces resultan los algoritmos. Lo
mismo sucede con el tipo de estructura de datos que utilice un programa, puesto que
cada una de ellas lleva implícitas unas limitaciones de eficiencia para sus operaciones
básicas de administración. Por eso, la etapa de diseño es tan importante dentro del
proceso de construcción de software, ya que va a determinar en muchos aspectos la
calidad del producto obtenido.
SELECCIÓN DE UN ALGORITMO
                                                                                             92
La escogencia de un algoritmo para resolver un problema es un proceso en el que se
deben tener en cuenta muchos factores, entre los cuales se pueden nombrar los
siguientes:

La complejidad en tiempo del algoritmo. Es una primera medida de la calidad de una
rutina, y establece su comportamiento cuando el número de datos que debe procesar
es muy grande. Es importante tenerla en cuenta, pero no es el único factor que se debe
considerar.

La complejidad en espacio del algoritmo. Es una medida de la cantidad de espacio que
necesita la rutina para representar la información. Sólo cuando esta complejidad resulta
razonable es posible utilizar este algoritmo con seguridad. Si las necesidades de
memoria crecen desmesuradamente con respecto al tamaño del problema, el rango de
utilidad del algoritmo es bajo y se debe descartar.

La dificultad de implementar el algoritmo. En algunos casos el algoritmo óptimo puede
resultar tan difícil de implementar, que no se justifique desarrollarlo para la aplicación
que se le va a dar a la rutina. Si su uso es bajo o no es una operación crítica del
programa que se está escribiendo, puede resultar mejor adoptar un algoritmo sencillo y
fácil de implementar, aunque no sea el mejor de todos.
93
       EFICIENCIA DE LOS ALGORITMOS

La eficiencia de un algoritmo, se suelen estudiar los recursos (memoria y tiempo) que
consume el algoritmo. El análisis de algoritmos se ha desarrollado para obtener valores
que de alguna forma indiquen (o especifiquen) la evolución del gasto de tiempo y
memoria en función del tamaño de los valores de entrada.
Portafolio
Portafolio
Portafolio

More Related Content

Viewers also liked

Pelicula
PeliculaPelicula
Peliculamaggyyy
 
Projet detude dans le cadre du m2 comores
Projet  detude dans le cadre du m2 comoresProjet  detude dans le cadre du m2 comores
Projet detude dans le cadre du m2 comoresGeraldine Delafosse
 
Séance plénière du sénat du 31 mai 2012
Séance plénière du sénat du 31 mai 2012Séance plénière du sénat du 31 mai 2012
Séance plénière du sénat du 31 mai 2012OBFG
 
21.06.12 lettre de la ministre de la justice
21.06.12 lettre de la ministre de la justice21.06.12 lettre de la ministre de la justice
21.06.12 lettre de la ministre de la justiceOBFG
 
El evangelio de la promesa
El evangelio de la promesaEl evangelio de la promesa
El evangelio de la promesaCarlos Henao
 
Ley de tributación municipal
Ley de tributación municipalLey de tributación municipal
Ley de tributación municipalAnghel Sucapuca
 
Desenclavement et mode de gouvernance
Desenclavement et mode de gouvernanceDesenclavement et mode de gouvernance
Desenclavement et mode de gouvernanceSonia Charbti
 
DSHS Köln: Inländereffekt der Basketball Bundesliga
DSHS Köln: Inländereffekt der Basketball BundesligaDSHS Köln: Inländereffekt der Basketball Bundesliga
DSHS Köln: Inländereffekt der Basketball BundesligawknD
 
Basic parliamentary procedures bilingual presentation sept 2012
Basic parliamentary procedures  bilingual presentation sept  2012Basic parliamentary procedures  bilingual presentation sept  2012
Basic parliamentary procedures bilingual presentation sept 2012OntarioEast
 
TDJ 2011 -V.Berry M.Boutet & S.Coavoux - Pratiques et dispositifs de jeu : n...
TDJ 2011 -V.Berry M.Boutet & S.Coavoux  - Pratiques et dispositifs de jeu : n...TDJ 2011 -V.Berry M.Boutet & S.Coavoux  - Pratiques et dispositifs de jeu : n...
TDJ 2011 -V.Berry M.Boutet & S.Coavoux - Pratiques et dispositifs de jeu : n...BasicomTdJ
 
30 10 Brit Se Ve Muy Bien
30 10 Brit Se Ve Muy Bien30 10 Brit Se Ve Muy Bien
30 10 Brit Se Ve Muy Bienacunato
 
Post impresionismo
Post impresionismoPost impresionismo
Post impresionismoErika Moreno
 
Nouvelle carte de printemps
Nouvelle carte de  printempsNouvelle carte de  printemps
Nouvelle carte de printempsLausanne-Moudon
 
Internet-Recherchemöglichkeiten für Romanisten und Historiker
Internet-Recherchemöglichkeiten für Romanisten und HistorikerInternet-Recherchemöglichkeiten für Romanisten und Historiker
Internet-Recherchemöglichkeiten für Romanisten und HistorikerSebastian_Nix
 

Viewers also liked (20)

Le studio ASA pour segmenter, décrire et publier des corpus audiovisuels – pr...
Le studio ASA pour segmenter, décrire et publier des corpus audiovisuels – pr...Le studio ASA pour segmenter, décrire et publier des corpus audiovisuels – pr...
Le studio ASA pour segmenter, décrire et publier des corpus audiovisuels – pr...
 
Pelicula
PeliculaPelicula
Pelicula
 
Projet detude dans le cadre du m2 comores
Projet  detude dans le cadre du m2 comoresProjet  detude dans le cadre du m2 comores
Projet detude dans le cadre du m2 comores
 
Séance plénière du sénat du 31 mai 2012
Séance plénière du sénat du 31 mai 2012Séance plénière du sénat du 31 mai 2012
Séance plénière du sénat du 31 mai 2012
 
21.06.12 lettre de la ministre de la justice
21.06.12 lettre de la ministre de la justice21.06.12 lettre de la ministre de la justice
21.06.12 lettre de la ministre de la justice
 
El evangelio de la promesa
El evangelio de la promesaEl evangelio de la promesa
El evangelio de la promesa
 
Ley de tributación municipal
Ley de tributación municipalLey de tributación municipal
Ley de tributación municipal
 
Chapter 9 overviewp
Chapter 9 overviewpChapter 9 overviewp
Chapter 9 overviewp
 
Desenclavement et mode de gouvernance
Desenclavement et mode de gouvernanceDesenclavement et mode de gouvernance
Desenclavement et mode de gouvernance
 
DSHS Köln: Inländereffekt der Basketball Bundesliga
DSHS Köln: Inländereffekt der Basketball BundesligaDSHS Köln: Inländereffekt der Basketball Bundesliga
DSHS Köln: Inländereffekt der Basketball Bundesliga
 
Notice
NoticeNotice
Notice
 
Basic parliamentary procedures bilingual presentation sept 2012
Basic parliamentary procedures  bilingual presentation sept  2012Basic parliamentary procedures  bilingual presentation sept  2012
Basic parliamentary procedures bilingual presentation sept 2012
 
TDJ 2011 -V.Berry M.Boutet & S.Coavoux - Pratiques et dispositifs de jeu : n...
TDJ 2011 -V.Berry M.Boutet & S.Coavoux  - Pratiques et dispositifs de jeu : n...TDJ 2011 -V.Berry M.Boutet & S.Coavoux  - Pratiques et dispositifs de jeu : n...
TDJ 2011 -V.Berry M.Boutet & S.Coavoux - Pratiques et dispositifs de jeu : n...
 
Diferencias
Diferencias Diferencias
Diferencias
 
H1 Prospection — French
H1 Prospection — FrenchH1 Prospection — French
H1 Prospection — French
 
Curado
Curado Curado
Curado
 
30 10 Brit Se Ve Muy Bien
30 10 Brit Se Ve Muy Bien30 10 Brit Se Ve Muy Bien
30 10 Brit Se Ve Muy Bien
 
Post impresionismo
Post impresionismoPost impresionismo
Post impresionismo
 
Nouvelle carte de printemps
Nouvelle carte de  printempsNouvelle carte de  printemps
Nouvelle carte de printemps
 
Internet-Recherchemöglichkeiten für Romanisten und Historiker
Internet-Recherchemöglichkeiten für Romanisten und HistorikerInternet-Recherchemöglichkeiten für Romanisten und Historiker
Internet-Recherchemöglichkeiten für Romanisten und Historiker
 

Similar to Portafolio

Similar to Portafolio (20)

Correccion del examen de m
Correccion del examen de mCorreccion del examen de m
Correccion del examen de m
 
Deber
DeberDeber
Deber
 
Correccion del examen de m
Correccion del examen de mCorreccion del examen de m
Correccion del examen de m
 
Asignaciones en java
Asignaciones en javaAsignaciones en java
Asignaciones en java
 
Asignaciones en java
Asignaciones en javaAsignaciones en java
Asignaciones en java
 
Deber funciones-numero-1
Deber funciones-numero-1Deber funciones-numero-1
Deber funciones-numero-1
 
Practica Programacion Ii 2003 2004
Practica Programacion Ii 2003 2004Practica Programacion Ii 2003 2004
Practica Programacion Ii 2003 2004
 
Derivada de una función
Derivada de una funciónDerivada de una función
Derivada de una función
 
Analisis de Algoritmos tarea 1
Analisis de Algoritmos tarea 1Analisis de Algoritmos tarea 1
Analisis de Algoritmos tarea 1
 
Datawarehouse
DatawarehouseDatawarehouse
Datawarehouse
 
Practica 9 Laboratorio de Computación para Ingenieros FI
Practica 9 Laboratorio de Computación para Ingenieros FIPractica 9 Laboratorio de Computación para Ingenieros FI
Practica 9 Laboratorio de Computación para Ingenieros FI
 
Funciones Clase1
Funciones Clase1Funciones Clase1
Funciones Clase1
 
Sub consultas
Sub consultasSub consultas
Sub consultas
 
Catalan
CatalanCatalan
Catalan
 
Qué es el sql
Qué es el sqlQué es el sql
Qué es el sql
 
Workshop Optimización Web
Workshop Optimización WebWorkshop Optimización Web
Workshop Optimización Web
 
Ronald medrano
Ronald medranoRonald medrano
Ronald medrano
 
Programación Dinámica
Programación DinámicaProgramación Dinámica
Programación Dinámica
 
Programacion
ProgramacionProgramacion
Programacion
 
Catalan
CatalanCatalan
Catalan
 

More from maggyyy

Metodología hibrida
Metodología hibridaMetodología hibrida
Metodología hibridamaggyyy
 
Consultoria una profecion
Consultoria una profecionConsultoria una profecion
Consultoria una profecionmaggyyy
 
Lecturas
LecturasLecturas
Lecturasmaggyyy
 
tipos de investigación
tipos de investigacióntipos de investigación
tipos de investigaciónmaggyyy
 
Conferencias
ConferenciasConferencias
Conferenciasmaggyyy
 
Conferencias
ConferenciasConferencias
Conferenciasmaggyyy
 
Presentan
PresentanPresentan
Presentanmaggyyy
 
Estilos de gestion de equipos jerarquicos y democraticos
Estilos de gestion de equipos jerarquicos y democraticosEstilos de gestion de equipos jerarquicos y democraticos
Estilos de gestion de equipos jerarquicos y democraticosmaggyyy
 
Tarea sumativa 4
Tarea sumativa 4Tarea sumativa 4
Tarea sumativa 4maggyyy
 
Tarea preguntas
Tarea preguntasTarea preguntas
Tarea preguntasmaggyyy
 
Tarea preguntas
Tarea preguntasTarea preguntas
Tarea preguntasmaggyyy
 
Tarea preguntas
Tarea preguntasTarea preguntas
Tarea preguntasmaggyyy
 
Tarea de magggyyyyyy
Tarea de magggyyyyyyTarea de magggyyyyyy
Tarea de magggyyyyyymaggyyy
 
Tarea de magggyyyyyy
Tarea de magggyyyyyyTarea de magggyyyyyy
Tarea de magggyyyyyymaggyyy
 
Tarea de magggyyyyyy
Tarea de magggyyyyyyTarea de magggyyyyyy
Tarea de magggyyyyyymaggyyy
 

More from maggyyy (19)

Metodología hibrida
Metodología hibridaMetodología hibrida
Metodología hibrida
 
Mapa
Mapa Mapa
Mapa
 
Guion
GuionGuion
Guion
 
Consultoria una profecion
Consultoria una profecionConsultoria una profecion
Consultoria una profecion
 
Lecturas
LecturasLecturas
Lecturas
 
tipos de investigación
tipos de investigacióntipos de investigación
tipos de investigación
 
F.O.D.A
F.O.D.AF.O.D.A
F.O.D.A
 
Conferencias
ConferenciasConferencias
Conferencias
 
Conferencias
ConferenciasConferencias
Conferencias
 
Presentan
PresentanPresentan
Presentan
 
Estilos de gestion de equipos jerarquicos y democraticos
Estilos de gestion de equipos jerarquicos y democraticosEstilos de gestion de equipos jerarquicos y democraticos
Estilos de gestion de equipos jerarquicos y democraticos
 
Tarea sumativa 4
Tarea sumativa 4Tarea sumativa 4
Tarea sumativa 4
 
Tarea3
Tarea3Tarea3
Tarea3
 
Tarea preguntas
Tarea preguntasTarea preguntas
Tarea preguntas
 
Tarea preguntas
Tarea preguntasTarea preguntas
Tarea preguntas
 
Tarea preguntas
Tarea preguntasTarea preguntas
Tarea preguntas
 
Tarea de magggyyyyyy
Tarea de magggyyyyyyTarea de magggyyyyyy
Tarea de magggyyyyyy
 
Tarea de magggyyyyyy
Tarea de magggyyyyyyTarea de magggyyyyyy
Tarea de magggyyyyyy
 
Tarea de magggyyyyyy
Tarea de magggyyyyyyTarea de magggyyyyyy
Tarea de magggyyyyyy
 

Portafolio

  • 1. INSTITUTO TECNOLÓGICO INSTITUTO TECNOLÓGICO 1 DE TUXTEPEC DE TUXTEPEC Ingeniería en informática Portafolio de evidencias Actiiviidad: Act v dad: Temas de la unidad Grupo “A” Turno: vespertino Presentado por: Margarita Antonio Gómez Profesor(a): Lic. Tomas Torres Ramírez 5 de diciembre 20011
  • 2. Índice 2 Introducción…………..……………………………………...3 Introducción a las estructuras de datos…………………………………4 Recursividad…………………………………………………………………….………….7 Estructuras lineales……………………………………………………………..…13 Estructuras no lineales………………………………………………………..…43 Métodos de ordenamiento……………………………………………………..63 Métodos de búsqueda…………………………………………………………….68 Análisis de los algoritmos……………………………………………………..……80 Conclusión…………………………………………………………………………………96
  • 3. INTRODUCCION 3 En este trabajo hablaremos de todos los temas del semestre, relacionados con la carrera de informática, en la materia estructura de datos. También algunos ejemplos de programas en java que complementa lo aprendido en este semestre.
  • 4. INTRODUCCIÓN A LAS ESTRUCTURAS 4 DE DATOS. TIPOS DE DATOS ABSTRACTOS (TDA) Un TDA es un tipo de dato definido por el programador que se puede manipular de un modo similar a los tipos de datos definidos por el sistema. Está formado por un conjunto válido de elementos y un número de operaciones primitivas que se pueden realizar sobre ellos. Ejemplo: - Definición del tipo Numero racional: Conjunto de pares de elementos (a,b) de tipo entero, con b<>0. MAPA CONCEPTUAL TAD
  • 5. PROGRAMA RECURSIONES ARCHIVO DE TEXTO 5 /* * Programa que utiliza metodos recursivos * Fibonaci, Factorial, Potencia y Division por estas sucesivas */ package packRecursiones; import javax.swing.*; public class claseRecursivas { public static int a, b, n; claseRecursivas(){ a=0; b=0; n=0; }; public static int potencia(int b, int n) { if(n == 0) { //caso base return 1; } else { return b * potencia(b, n -1); // llamada recursiva } } public static int factorial(int n){ if(n==0) return 1; //Caso base else return n*factorial(n-1); //Formula recursiva } public static int fibonaci( int n){ if(n==1 || n==2) return 1; else return fibonaci(n-1)+fibonaci(n-2); } public static int division( int a, int b) { if(b > a) return 0; else return division(a-b, b) + 1; } public static void menu(){ String k=""; int e=5, r=0; k+="Menú de operacionesnn1) Fibonacin2) Factorialn3) potencian"; k+="4) Division por restas sucesivasn5) Salir"; k+="nnElige una opción"; do{ e=Integer.parseInt(JOptionPane.showInputDialog(k)); switch(e){ case 1: n=Integer.parseInt(JOptionPane.showInputDialog("Valor maximo para fibonaci"));
  • 6. r=fibonaci(n); 6 JOptionPane.showMessageDialog(null,"Los numero fibonaci son "+r); break; case 2: n=Integer.parseInt(JOptionPane.showInputDialog("Valor para obtener el factorial")); r=factorial(n); JOptionPane.showMessageDialog(null,"El factorial es. "+r); break; case 3: b=Integer.parseInt(JOptionPane.showInputDialog("Valor para la base")); n=Integer.parseInt(JOptionPane.showInputDialog("Valor para potencia")); r=potencia(b,n); JOptionPane.showMessageDialog(null,"La potencia es: "+r); break; case 4: a=Integer.parseInt(JOptionPane.showInputDialog("Valor para dividendo")); b=Integer.parseInt(JOptionPane.showInputDialog("Valor para la divisor")); r=division(b,n); JOptionPane.showMessageDialog(null,"El resultado de la division es: "+r); break; case 5: JOptionPane.showMessageDialog(null,"Fin del programa"); } }while(e!=5); }; public static void main(String[] args) { // claseRecursivas obj1=new claseRecursivas(); claseRecursivas.menu(); } }
  • 7. RECURSIVIDAD 7 RESUMEN La recursividad es una técnica de programación importante. Se utiliza para realizar una llamada a una función desde la misma función. La recursividad consiste en realizar una definición de un concepto en términos del propio concepto que se está definiendo. La recursividad forma parte del repertorio para resolver problemas en Computación y es de los métodos más poderosos y usados. Los algoritmos recursivos ofrecen soluciones estructuradas, modulares y elegantemente simples. La recursividad es un concepto fundamental en matemáticas y en computación. Una definición recursiva dice cómo obtener conceptos nuevos empleando el mismo concepto que intenta describir. En toda situación en la cual la respuesta pueda ser expresada como una secuencia de movimientos, pasos o transformaciones gobernadas por un conjunto de reglas no ambiguas, la fórmula recursiva es un buen candidado para resolver el problema. Ejemplos: Los números naturales se pueden definir de la siguiente forma: 0 es un Número natural y el sucesor de un número natural es también un número natural. El factorial de un número natural n, es 1 si dicho número es 0, o n multiplicado por el factorial del número n-1, en caso contrario. La n-ésima potencia de un número x, es 1 si n es igual a 0, o el producto de x por la potencia (n-1)-ésima de x, cuando n es mayor que 0. Diseño de módulos recursivos. Módulo M con una llamada a sí mismo: módulo recursivo directo. Módulo M con una llamada a otro F, que hace una llamada a M: Módulo recursivo indirecto. Ejemplo: Implementación del factorial de un número. public long factorial (long n)
  • 8. { 8 if (n == 0) return 1; else return n * factorial(n-1); } Búsqueda de soluciones recursivas: cuatro preguntas básicas. ¿Cómo se puede definir el problema en términos de uno o más problemas más pequeños del mismo tipo que el original? ¿Qué instancias del problema harán de caso base? Conforme el problema se reduce de tamaño ¿se alcanzará el caso base? ¿Cómo se usa la solución del caso base para construir una solución correcta al problema original? Ejemplo: La pila del ordenador y la recursividad. La memoria de un ordenador a la hora de ejecutar un programa queda dividida en dos partes: ·la zona donde se almacena el código del programa y ·la zona donde se guardan los datos: pila (utilizada para llamadas recursivas).
  • 9. 9 Programa principal llama a una rutina M, se crea en la pila de un registro de activación o entorno, E almacena constantes, variables locales y parámetros formales. Estos registros se van apilando conforme se llama sucesivamente desde una función a otras. Cuando finaliza la ejecución, se va liberando el espacio Profundidad de la recursión: número de registros de activación en la pila en un momento dado. Problema: Si profundidad es muy grande => desbordamiento pila. Representación gráfica del registro de activación: Ejemplo de evolución de la pila: impresión de palabras en sentido contrario al leído. Función recursiva: imp_OrdenInverso. Parámetros: número de palabras que quedan por leer (n). Respuestas: 1. Se lee la primera palabra, y recursivamente se llama a la función para que lea e imprima el resto de palabras, para que finalmente se imprima la primera leída. 2. El caso base: cuando sólo quede una palabra (n== 1), en cuyo caso se imprimirá directamente. 3. Como en cada llamada recursiva hay que leer una palabra menos del total, en n-1 llamadas se habrá alcanzado el caso base. 4. Una vez se haya alcanzado el caso base al devolver continuamente el control a las funciones invocantes se irán imprimiendo de atrás a delante las palabras obtenidas desde la entrada estándar. public void imp_OrdenInverso(int n) { String palabra; if (n == 1) { palabra =Console.in.readln(); System.out.println(palabra); } else { palabra =Console.in.readln(); imp_OrdenInverso(n-1);
  • 10. Las definiciones recursivas de funciones en matemáticas que tienen como 10 argumentos números enteros, se llaman relaciones de recurrencia. Forma de una ecuación de recurrencia: coar +c1ar-1 + c2ar-2 + ....+ ckar-k = f(r) función matemática discreta donde ci son constantes, es llamada una ecuación de recurrencia de coeficientes constantes de orden k, condicionada a que c 0 y ck = 0. Una definición recursiva dice cómo obtener conceptos nuevos empleando el mismo concepto que intenta definir. El poder de la recursividad es que los procedimientos o conceptos complejos pueden expresarse de una forma simple. Un razonamiento recursivo tiene dos partes: la base y la regla recursiva de construcción. La base no es recursiva y es el punto tanto de partida como de terminación de la definición. Ejemplo: Base: La secuenciación, iteración condicional y selección son estructuras válidas de control que pueden ser consideradas como enunciados. Regla recursiva: Las estructuras de control que se pueden formar combinando de manera válida la secuenciación iteración condicional y selección también son válidos. Un conjunto de objetos está definido recursivamente siempre que: (B) algunos elementos del conjunto se especifican explícitamente 1. El procedimiento se llama a si mismo 2. El problema se resuelve, resolviendo el mismo problema pero de tamaño menor 3. La manera en la cual el tamaño del problema disminuye asegura que el caso base eventualmente se alcanzará La recursividad es un método poderoso usado en inteligencia artificial, su poder es que algunos conceptos complejos pueden expresarse en una forma simple. Una definición recursiva difiere de una definición circular en que tiene una forma de escapar de su expansión infinita. Este escape se encuentra en la definición o porción no recursiva o terminal de la definición. Las fórmulas recursivas pueden aplicarse a situaciones tales como prueba de teoremas, solución de problemas combinatorios, algunos acertijos, etc. Cuando una llamada recursiva es la última posición ejecutada del procedimiento se llama recursividad de cola, recursividad de extremo final o recursión e extremo de cola. Cuando un procedimiento incluye una llamada a si mismo se conoce como recursión directa.
  • 11. 11 Cuando un procedimiento llama a otro procedimiento y este causa que el procedimiento original sea invocado, se conoce como recursión indirecta. Al principio algunas personas se sienten un poco incómodas con la recursividad, tal vez porque da la impresión de ser un ciclo infinito, pero en realidad es menos peligrosa una recursión infinita que un ciclo infinito, ya que una recursividad infinita pronto se queda sin espacio y termina el programa, mientras que la iteración infinita puede continuar mientras no se termine en forma manual. Cuando un procedimiento recursivo se llama recursivamente a si mismo varias veces, para cada llamada se crean copias independientes de las variables declaradas en el procedimiento. PROGRAMA CON MENÚ Y MÉTODOS RECURSIVOS Programa que llama a los métodos recursivos package packRecursiones; import javax.swing.*; public class claseRecursivas { public static int a, b, n; claseRecursivas(){ a=0; b=0; n=0; }; public static int potencia(int b, int n) { if(n == 0) { //caso base return 1; } else { return b * potencia(b, n -1); // llamada recursiva }
  • 12. } 12 public static int factorial(int n){ if(n==0) return 1; //Caso base else return n*factorial(n-1); //Formula recursiva } public static int fibonaci( int n){ if(n==1 || n==2) return 1; else return fibonaci(n-1)+fibonaci(n-2); } public static int division( int a, int b) { if(b > a) { return 0; } else { return division(a-b, b) + 1; } } public static void menu(){ String k=""; int e=5, r=0; k+=" MENU DE OPERACIONESnn1) Fibonacin2) Factorialn3) potencian4) Division por restas sucesivasn5) SalirnnElige una opción"; do{ e=Integer.parseInt(JOptionPane.showInputDialog(k)); switch(e){
  • 13. case 1: 13 n=Integer.parseInt(JOptionPane.showInputDialog("Valor maximo para fibonaci")); r=fibonaci(n); JOptionPane.showMessageDialog(null,"Los numero fibonaci son: "+r); break; case 2: n=Integer.parseInt(JOptionPane.showInputDialog("Valor para obtener el factorial")); r=factorial(n); JOptionPane.showMessageDialog(null,"El factorial de "+n+"!="+r); break; case 3: b=Integer.parseInt(JOptionPane.showInputDialog("Valor para la base")); n=Integer.parseInt(JOptionPane.showInputDialog("Valor para potencia")); r=potencia(b,n); JOptionPane.showMessageDialog(null,"La potencia es: "+r); break; case 4: b=Integer.parseInt(JOptionPane.showInputDialog("Valor para dividendo")); n=Integer.parseInt(JOptionPane.showInputDialog("Valor para la divisor")); r=division(b,n); JOptionPane.showMessageDialog(null,"El resultado de la division es: "+r); break; case 5: JOptionPane.showMessageDialog(null,"Fin del programa... :)"); }
  • 14. }while(e!=5); 14 }; public static void main(String[] args) { // claseRecursivas obj1=new claseRecursivas(); claseRecursivas.menu(); } } PANTALLAS
  • 15. 15
  • 16. ESTRUCTURAS LINEALES 16 TRABAJO DE INVESTIGACION INTRODUCCIÓN Las estructuras lineales son importantes porque aparecen con mucha frecuencia en situaciones de la vida: Una cola de clientes de un banco, las instrucciones de un programa, los caracteres de una cadena o las páginas de un libro Características: existe un único elemento, llamado primero, existe un único elemento, llamado último, cada elemento, excepto el primero, tiene un único predecesor y cada elemento, excepto el último, tiene un único sucesor. Operaciones: crear la estructura vacía, insertar un elemento, borrar y obtener un elemento. Para definir claramente el comportamiento de la estructura es necesario determinar en qué posición se inserta un elemento nuevo y qué elemento se borra o se obtiene. Principales estructuras lineales: pilas, colas y listas.
  • 17. ESTRUCTURAS LINEALES 17 Listas (estructura de datos) Una lista enlazada es una de las estructura de datos fundamentales, y puede ser usada para implementar otras estructuras de datos. Consiste en una secuencia de nodos, en los que se guardan campos de datos arbitrarios y una o dos referencias (punteros) al nodo anterior y/o posterior. El principal beneficio de las listas enlazadas respecto a los array convencionales es que el orden de los elementos enlazados puede ser diferente al orden de almacenamiento en la memoria o el disco, permitiendo que el orden de recorrido de la lista sea diferente al de almacenamiento. Una lista enlazada es un tipo de dato auto-referenciado porque contienen un puntero o link a otro dato del mismo tipo. Las listas enlazadas permiten inserciones y eliminación de nodos en cualquier punto de la lista en tiempo constante (suponiendo que dicho punto está previamente identificado o localizado), pero no permiten un acceso aleatorio. Existen diferentes tipos de listas enlazadas: Lista Enlazadas Simples, Listas Doblemente Enlazadas, Listas Enlazadas Circulares y Listas Enlazadas Doblemente Circulares. Lenguajes imperativos u orientados a objetos tales como C o C++ y Java, respectivamente, disponen de referencias para crear listas enlazadas. Operaciones básicas con listas El alterador de lista es un objeto que permite recorrer los elementos de la lista y operar con ella
  • 18. Dispone de un cursor que se sitúa entre dos elementos: previo ypróximo 18 Las operaciones básicas de las listas son: • Constructor: Crea la lista vacía • hazNula: Elimina todos los elementos de la lista, dejándolo vacía • estaVacia: Si la lista está vacía retorna true. En otro caso, retorna false • Tamaño: Retorna un entero que dice cuántos elementos hay en la lista • iteradorDeLista: Retorna un iterador de lista asociado a la lista, para poder recorrer sus elementos Operaciones básicas de los iteradores de lista Inicialmente el iterador de lista tiene el cursor situado antes del primer elemento Las operaciones básicas que se pueden hacer con un iterador de lista son: añade: Añade elElemento a la lista entre los elementos previo y próximo, si existen; si no existen, lo añade como el único elemento de la lista. El cursor queda situado justo después del nuevo elemento. Borra: borra el último elemento obtenido de la lista con previo() o proximo(). Si no hay tal elemento, lanza estadoIncorrecto. cambiaElemento: modifica el último elemento obtenido de la lista con previo() o proximo() sustituyéndolo por el nuevoElemento. Si no hay tal elemento, o si se ha llamado a borra () o añade() después de previo() o proximo(), lanza estadoIncorrecto.
  • 19. hayPrevio: Retorna true si existe un elemento previo al cursor y false en 19 caso contrario. hayProximo: Retorna true si existe un elemento proximo al cursor y false en caso contrario. Previo: Retorna el elemento previo al cursor, y hace que éste retroceda un elemento en la lista. Si no existe elemento previo, lanza noExiste. Próximo: Retorna el elemento próximo al cursor, y hace que éste avance un elemento en la lista. Si no existe elemento próximo, lanza noExiste. Observar que si se llama a previo () después de llamar a proximo() se obtiene el mismo elemento. La interfaz List de Java public interface List<E> extends Collection<E> { // Positional access E get(int index); E set(int index, E element); //optional boolean add(E element); //optional void add(int index, E element); //optional E remove(int index); //optional boolean addAll(int index, Collection<? extends E> c); //optional // Search int indexOf(Object o); int lastIndexOf(Object o); // Iteration ListIterator<E> listIterator(); ListIterator<E> listIterator(int index); // Range-view List<E> subList(int from, int to); }
  • 20. Tipos de listas 20 Listas simples enlazadas La lista enlazada básica es la lista enlazada simple la cual tiene un enlace por nodo. Este enlace apunta al siguiente nodo en la lista, o al valor NULL o a la lista vacía, si es el último nodo. Una lista enlazada simple contiene dos valores: el valor actual del nodo y un enlace al siguiente nodo Lista Doblemente Enlazada Un tipo de lista enlazada más sofisticado es la lista doblemente enlazada o lista enlazadas de dos vías. Cada nodo tiene dos enlaces: uno apunta al nodo anterior, o apunta al valor NULL o a la lista vacía si es el primer nodo; y otro que apunta al siguiente nodo siguiente, o apunta al valor NULL o a la lista vacía si es el último nodo. Una lista doblemente enlazada contiene tres valores: el valor, el link al nodo siguiente, y el link al anterior En algún lenguaje de muy bajo nivel, XOR-Linking ofrece una vía para implementar listas doblemente enlazadas, usando una sola palabra para ambos enlaces, aunque el uso de esta técnica no se suele utilizar.
  • 21. 21 Listas enlazadas circulares En una lista enlazada circular, el primer y el último nodo están unidos juntos. Esto se puede hacer tanto para listas enlazadas simples como para las doblemente enlazadas. Para recorrer un lista enlazada circular podemos empezar por cualquier nodo y seguir la lista en cualquier dirección hasta que se regrese hasta el nodo original. Desde otro punto de vista, las listas enlazadas circulares pueden ser vistas como listas sin comienzo ni fin. Este tipo de listas es el más usado para dirigir buffers para “ingerir” datos, y para visitar todos los nodos de una lista a partir de uno dado. Una lista enlazada circular que contiene tres valores enteros Listas enlazadas circulares simples Cada nodo tiene un enlace, similar al de las listas enlazadas simples, excepto que el siguiente nodo del último apunta al primero. Como en una lista enlazada simple, los nuevos nodos pueden ser solo eficientemente insertados después de uno que ya tengamos referenciado. Por esta razón, es usual quedarse con una referencia solamente al último elemento en una lista enlazada circular simple, esto nos permite rápidas inserciones al principio, y también permite accesos al primer nodo desde el puntero del último nodo. Lista Enlazada Doblemente Circular
  • 22. 22 En una lista enlazada doblemente circular, cada nodo tiene dos enlaces, similares a los de la lista doblemente enlazada, excepto que el enlace anterior del primer nodo apunta al último y el enlace siguiente del último nodo, apunta al primero. Como en una lista doblemente enlazada, las inserciones y eliminaciones pueden ser hechas desde cualquier punto con acceso a algún nodo cercano. Aunque estructuralmente una lista circular doblemente enlazada no tiene ni principio ni fin, un puntero de acceso externo puede establecer el nodo apuntado que está en la cabeza o al nodo cola, y así mantener el orden tan bien como en una lista doblemente enlazada. Aplicaciones de las listas Las listas enlazadas son usadas como módulos para otras muchas estructuras de datos, tales como pilas, colas y sus variaciones. El campo de datos de un nodo puede ser otra lista enlazada. Mediante este mecanismo, podemos construir muchas estructuras de datos enlazadas con listas; esta practica tiene su origen en el lenguaje de programación Lisp, donde las listas enlazadas son una estructura de datos primaria (aunque no la única), y ahora es una característica común en el estilo de programación funcional. A veces, las listas enlazadas son usadas para implementar arrays asociativos, y estas en el contexto de las llamadas listas asociativas. Hay pocas ventajas en este uso de las listas enlazadas; hay mejores formas de implementar éstas estructuras, por ejemplo con árboles binarios de búsqueda equilibrados. Sin embargo, a veces una lista enlazada es dinámicamente creada fuera de un subconjunto propio de nodos semejante a un árbol, y son usadas más eficientemente para recorrer ésta serie de datos.
  • 23. BIBLIOGRAFIA 23 Cómo programar en Java - Página 978 Harvey M. Deitel, Paul J. Deitel, Guillermo Trujano Mendoza – 2004 http://estdatosgrupo8a.blogspot.com/2009/03/estructuras-lineales.html
  • 24. 24 PROGRAMA DE LISTAS Subir el programa de listas armado con los métodos que están en la presentación. Programa: : import javax.swing.JOptionPane; public class ListEnlaza{ static ListEnlaza accion=new ListEnlaza(); int n=Integer.parseInt(JOptionPane.showInputDialog("TAMAÑO")); public static void main(String[] args) { int opc=0; while(true){ opc=Integer.parseInt(JOptionPane.showInputDialog(null,"__¿ACCION A REALIZAR?__nn"+"1. INCERTAR AL INICIOn"+"2. INCERTAR AL FINALn"+"3. BORRAR AL FINAL n"+"4.BORRAR AL INICIO n"+"5. TAMAÑO DE LA LISTA n"+"6. DATOS DE LA LISTA FINALn"+"7. DATOS DE LA LISTA INICIO n"+"8. SALIRn")); switch(opc){ case 1: accion.Insercion(); break;
  • 25. case 2: accion.addLast(100); 25 break; case 3: accion.deleteLast(); break; case 4: accion.Eliminar(); break; case 5: accion.tamano(); break; case 6: accion.imprimir(); break; case 7: accion.Recorrido(); break; case 8: System.exit(0); break; default: JOptionPane.showMessageDialog(null,"OPCION INVALIDA","ERROR", JOptionPane.ERROR_MESSAGE);; break;}} } public class Nodo { Nodo nodoDer; int dato; public Nodo(int dato) { this.dato = dato; this.nodoDer = null; }}
  • 26. private Nodo primero; 26 private Nodo ultimo; private int tamano; public ListEnlaza() { this.primero = null; this.ultimo = null; this.tamano = 0; } //Metodo utilizado para denotar que la lista se encuentra vacia. public boolean siVacio() { return (this.primero == null); } //Metodo para agregar al inicio String LIS[]=new String [n]; int Final=-1; int Frente=0; int Max=LIS.length-1; public void Insercion()throws ArrayIndexOutOfBoundsException{ if(Final==Max){ JOptionPane.showMessageDialog(null,"¡DESBORDAMIENTO, LLENA!","ERROR", JOptionPane.ERROR_MESSAGE);; }
  • 27. else { 27 Frente=-1; Final++; LIS[Final]=JOptionPane.showInputDialog(null,"INSERTA UN DATO:"); JOptionPane.showMessageDialog(null,"SE INSERTO EL DATO ( "+LIS[Final]+" )"); } } public void Eliminar()throws ArrayIndexOutOfBoundsException{ if(Final==-1) JOptionPane.showMessageDialog(null,"¡VACIA","ERROR!", JOptionPane.ERROR_MESSAGE); else{ JOptionPane.showMessageDialog(null, "¡DATO ("+LIS[ 0 ]+") ELIMINADO!"); for (int i=0; i<Final-1; i++) LIS[ i ] = LIS[ i+1 ]; Final--;}} public void Recorrido()throws ArrayIndexOutOfBoundsException{ if(Final==-1){ JOptionPane.showMessageDialog(null,"COLA VACIA","ERROR", JOptionPane.ERROR_MESSAGE); } else{ String Recorrido= "";
  • 28. Recorrido+= "n ELEMENTOS EN COLA:"; 28 for(int i=0; i<=Final; i++) Recorrido+= "nLISTA["+i+"] = "+LIS[i]; JOptionPane.showMessageDialog(null, Recorrido); } } //Metodo para agregar al final de la lista. public ListEnlaza addLast(int dato) { dato=Integer.parseInt(JOptionPane.showInputDialog("Ingresa un dato")); if(siVacio()) { Nodo nuevo = new Nodo(dato); primero = nuevo; ultimo = nuevo; nuevo.nodoDer = nuevo; } else { Nodo nuevo = new Nodo(dato); nuevo.nodoDer = null; ultimo.nodoDer = nuevo; ultimo = nuevo; } this.tamano++; return this; }
  • 29. //Metodo para borrar al final de la lista. 29 public Nodo deleteLast() { Nodo eliminar = null; if(siVacio()) { JOptionPane.showMessageDialog(null,"LA LISTA SE ENCUENTRA VACIA","ERROR", JOptionPane.ERROR_MESSAGE); return null; } if(primero == ultimo) { primero = null; ultimo = null; } else { Nodo actual = primero; while(actual.nodoDer != ultimo) { actual = actual.nodoDer; } eliminar = actual.nodoDer; actual.nodoDer = null; ultimo = actual; } JOptionPane.showMessageDialog(null,"SE ELIMINO EL DATO DE LA POSICION ( "+tamano+" )");
  • 30. this.tamano--; 30 return eliminar; } //Metodo que imprime el tamaño de la lista. public void tamano() { JOptionPane.showMessageDialog(null, "El tamaño es:n " + this.tamano); } //Metodo que imprime la lista y los valores ingresados. public Nodo imprimir() { Nodo imprimir = null; if(siVacio()) { JOptionPane.showMessageDialog(null,"LA LISTA SE ENCUENTRA VACIA","ERROR", JOptionPane.ERROR_MESSAGE); return null; } if(tamano != 0) { Nodo temp = primero; String str = ""; for(int i = 0; i < this.tamano; i++) { str = str + temp.dato + "n"; temp = temp.nodoDer; } JOptionPane.showMessageDialog(null, "Lista:n" + str); }
  • 31. return imprimir; 31 } } Pantallas:
  • 32. 32 Hacer un programa que utilice las operaciones sobre las pilas o colas (push, pop y recorrido). import javax.swing.*; public class Colas { static Cola accion=new Cola(); public static void main(String[] args) { int opc=0; while(true){ opc=Integer.parseInt(JOptionPane.showInputDialog(null,"__¿ACCION A REALIZAR?__nn" +"1. INSERCIÓNn"+"2. RECORRIDOn"+"3. BUSCARn"+"4. ELIMINAR n"+"5. SALIRn")); switch(opc){ case 1: accion.Insercion(); break; case 2: accion.Recorrido(); break; case 3: accion.Buscar(); break; case 4: accion.Eliminar(); break; case 5: System.exit(0); break; default: JOptionPane.showMessageDialog(null,"OPCION INVALIDA","ERROR",
  • 33. JOptionPane.ERROR_MESSAGE);; 33 break; } } } } class Cola{ int n=Integer.parseInt(JOptionPane.showInputDialog("INTRODUCE EL TAMAÑO DE LA COLA")); String cola[]=new String [n]; int Final=-1; int Frente=0; int Max=cola.length-1; public void Insercion()throws ArrayIndexOutOfBoundsException{ if(Final==Max){ JOptionPane.showMessageDialog(null,"¡DESBORDAMIENTO, COLA LLENA!","ERROR", JOptionPane.ERROR_MESSAGE);; } else { Frente=-1; Final++; cola[Final]=JOptionPane.showInputDialog(null,"INSERTA UN DATO:"); JOptionPane.showMessageDialog(null,"SE INSERTO EL DATO ( "+cola[Final]+" )"); }
  • 34. } 34 public void Recorrido()throws ArrayIndexOutOfBoundsException{ if(Final==-1){ JOptionPane.showMessageDialog(null,"COLA VACIA","ERROR", JOptionPane.ERROR_MESSAGE); } else{ String Recorrido= ""; Recorrido+= "n ELEMENTOS EN COLA:"; for(int i=0; i<=Final; i++) Recorrido+= "ncola["+i+"] = "+cola[i]; JOptionPane.showMessageDialog(null, Recorrido); } } public void Buscar()throws ArrayIndexOutOfBoundsException{ int ban=0; String encontrado=""; String elemento=JOptionPane.showInputDialog(null,"¿QUE DATO BUSCAS?"); for(int i=0;i<=Final;i++){ if(elemento.equals(cola[i])){ encontrado=encontrado+cola[i]; ban=ban+1; } }
  • 35. if(ban==0){ 35 JOptionPane.showMessageDialog(null,"DATO ("+elemento+") NO ENCONTRADO ","ERROR", JOptionPane.ERROR_MESSAGE); } else{ JOptionPane.showMessageDialog(null, ""+ban+" DATO ENCONTRADO= ( "+elemento+" )"); } } public void Eliminar()throws ArrayIndexOutOfBoundsException{ if(Final==-1) JOptionPane.showMessageDialog(null,"¡COLA VACIA","ERROR!", JOptionPane.ERROR_MESSAGE); else{ JOptionPane.showMessageDialog(null, "¡DATO ("+cola[ 0 ]+") ELIMINADO!"); for (int i=0; i<Final-1; i++) cola[ i ] = cola[ i+1 ]; Final--; } } }
  • 36. Programa de pila personalizado 36 mport javax.swing.*; public class Pila2{ class Nodo { int info; Nodo sig; } private Nodo raiz; public Pila2 (){ raiz=null; } public void insertar(int x){ Nodo nuevo; nuevo=new Nodo (); nuevo.info=x; if(raiz==null) { nuevo.sig=null; raiz=nuevo; } else {
  • 37. nuevo. sig=raiz; 37 raiz=nuevo; } } public int extraer () { if(raiz!=null) { int informacion=raiz. info; raiz = raiz. sig; return informacion; } else { return Integer.MAX_VALUE; } } public void imprimir (){ Nodo reco=raiz; System.out.println("listado de todos los elementos de la pila."); while(reco!=null){ System.out.print(reco.info+"-"); reco=reco.sig;
  • 38. } 38 System.out.println(); } public static void main (String[] ar) { Pila2 pila1=new Pila2(); int op=4,dato; do { String menu ="nn menu de opciones nn1)Insertarn2)Eliminarn3)Listan4)SalirnnElije una opcion:"; op=Integer.parseInt(JOptionPane.showInputDialog(menu)); switch(op){ case 1: dato=Integer.parseInt(JOptionPane.showInputDialog("Dato a insertar")); pila1.insertar(dato); break; case 2:dato=pila1.extraer(); JOptionPane.showMessageDialog(null,"El dato eliminado es:"+dato); break; case 3:pila1.imprimir(); break; case 4:JOptionPane.showMessageDialog(null,"Fin del programa"); }
  • 39. } 39 while(op!=4) } } Hacer un programa que manipule una cola. Debe tener los métodos para insertar, eliminar y recorrer la cola en una lista enlazada. Colas Las colas son una subclase de las listas lineales que siguen el orden FIFO (First Input First Output - Primero en entrar Primero en salir). Este orden es que siguen las filas que hacemos en la vida cotidiana al ir al banco, las tortillas, etc. En las colas la inserción se hace al final y la eliminación se hace al frente, por lo que se hace necesario el uso de una variable Primero y otra variable Último por medio de las cuales se llevaran a cabo las operaciones. Import javax.swing.*; public class colas1 { static cola accion=new cola(); public static void main(string[] args) { int opc=0; while(true){ opc=integer.parseint(joptionpane.showinputdialog(null,"__¿accion a realizar?__nn" +"1. Insertarn"+"2. Recorrern"+"3. Eliminar n"+"4. Salirn"));
  • 40. switch(opc){ 40 case 1: accion.insercion(); break; case 2: accion.recorrido(); break; case 4: accion.eliminar(); break; case 5: system.exit(0); break; default: joptionpane.showmessagedialog(null,"opcion invalida","error", joptionpane.error_message);; break; } } } } Class cola{ int n=integer.parseint(joptionpane.showinputdialog("introduce el tamaño de la cola")); string cola[]=new string [n]; int final=-1; int frente=0; int max=cola.length-1; public void insercion()throws arrayindexoutofboundsexception{
  • 41. if(final==max){ 41 joptionpane.showmessagedialog(null,"¡desbordamiento, cola llena!","error", joptionpane.error_message);; } else { frente=-1; final++; cola[final]=joptionpane.showinputdialog(null,"inserta un dato:"); joptionpane.showmessagedialog(null,"se inserto el dato ( "+cola[final]+" )"); } } public void recorrido()throws arrayindexoutofboundsexception{ if(final==-1){ joptionpane.showmessagedialog(null,"cola vacia","error", joptionpane.error_message); } else{ string recorrido= ""; recorrido+= "n elementos en cola:"; for(int i=0; i<=final; i++) recorrido+= "ncola["+i+"] = "+cola[i]; joptionpane.showmessagedialog(null, recorrido); } }
  • 42. Public void eliminar()throws arrayindexoutofboundsexception{ 42 if(final==-1) joptionpane.showmessagedialog(null,"¡cola vacia","error!", joptionpane.error_message); else{ joptionpane.showmessagedialog(null, "¡dato ("+cola[ 0 ]+") eliminado!"); for (int i=0; i<final-1; i++) cola[ i ] = cola[ i+1 ]; final--; } } }
  • 43. ESTRUCTURAS NO LINEALES 43 Arboles Concepto de arboles Un árbol se define como una colección de nodos organizados en forma recursiva. Cuando hay 0 nodos se dice que el árbol está vacío, en caso contrario el árbol consiste en un nodo denominado raíz, el cual tiene 0 o más referencias a otros árboles, conocidos como subárboles. Las raíces de los subárboles se denominan hijos de la raíz, y consecuentemente la raíz se denomina padre de las raíces de sus subárboles. Una visión gráfica de esta definición recursiva se muestra en la siguiente figura: Los nodos que no poseen hijos se denominan hojas. Dos nodos que tienen el padre en común se denominan hermanos. Un camino entre un nodo n1 y un nodo nk está definido como la secuencia de nodos n1, n2,..., nk tal que ni es padre de ni+1, 1 <= i < k. El largo del camino es el número de referencias que componen el camino, que para el ejemplo son k-1. Existe un camino desde cada nodo del árbol a sí mismo y es de largo 0. Nótese que en un árbol existe un único camino desde la raíz hasta cualquier otro nodo del árbol. A partir del concepto de camino se definen los conceptos de ancestro y descendiente: un nodo n es ancestro de un nodo m si existe un camino desde n a m; un nodo n es descendiente de un nodo m si existe un camino desde m a n. Se define la profundidad del nodo nk como el largo del camino entre la raíz del arbol y el nodo nk. Esto implica que la profundidad de la raíz es siempre 0. La altura de un nodo nk es el máximo largo de camino desde nk hasta alguna hoja. Esto implica que la altura de toda hoja es 0. La altura de un árbol es igual a la altura de la raíz, y tiene el mismo valor que la profundidad de la hoja más profunda. La altura de un árbol vacío se define como -1. La siguiente figura muestra un ejemplo de los conceptos previamente descritos:
  • 44. 44 A es la raíz del árbol. A es padre de B, C y D. E y F son hermanos, puesto que ambos son hijos de B. E, J, K, L, C, P, Q, H, N y O son las hojas del árbol. El camino desde A a J es único, lo conforman los nodos A-B-F-J y es de largo 3. D es ancestro de P, y por lo tanto P es descendiente de D. L no es descendiente de C, puesto que no existe un camino desde C a L. La profundidad de C es 1, de F es 2 y de Q es 4. La altura de C es 0, de F es 1 y de D es 3. La altura del árbol es 4 (largo del camino entre la raíz A y la hoja más profunda, P o Q). Clasificación de árboles binarios: 1.-arbol binario distinto: Se dice que un árbol es distinto cuando su estructura grafica es diferente. 2.-arbol binario similar.- Se dice que un árbol es similar cuando su estructura grafica es idéntica pero la información que contiene entre sus nodos es diferente. 3.-arbol binario equivalente.-Son aquellos que su estructura grafica es idéntica pero además la información entre sus nodos. 4.-arbol binario completo.-son aquellos que todos nus nodos excepto el último nivel tienen sus dos hijos. 5.-arbol binario lleno: Es aquel que tiene su número máximo de posibles nodos. Operaciones básicas sobre arboles binarios Árboles binarios
  • 45. Un árbol binario es un árbol en donde cada nodo posee 2 referencias a subárboles 45 (ni más, ni menos). En general, dichas referencias se denominan izquierda y derecha, y consecuentemente se define el subárbol izquierdo y subárbol derecho del arbol. Como en toda estructura de datos hay dos operaciones básicas, inserción y eliminación. Inserción El procedimiento de inserción en un árbol binario de búsqueda es muy sencillo, únicamente hay que tener cuidado de no romper la estructura ni el orden del árbol. Cuando se inserta un nuevo nodo en el árbol hay que tener en cuenta que cada nodo no puede tener más de dos hijos, por esta razón si un nodo ya tiene 2 hijos, el nuevo nodo nunca se podrá insertar como su hijo. Con esta restricción nos aseguramos mantener la estructura del árbol, pero aún nos falta mantener el orden. Para localizar el lugar adecuado del árbol donde insertar el nuevo nodo se realizan comparaciones entre los nodos del árbol y el elemento a insertar. El primer nodo que se compara es la raíz, si el nuevo nodo es menor que la raíz, la búsqueda prosigue por el nodo izquierdo de éste. Si el nuevo nodo fuese mayor, la búsqueda seguiría por el hijo derecho de la raíz. Este procedimiento es recursivo, y su condición de parada es llegar a un nodo que no tenga hijo en la rama por la que la búsqueda debería seguir. En este caso el nuevo nodo se inserta en ese hueco, como su nuevo hijo. Vamos a verlo con un ejemplo sobre el siguiente árbol:
  • 46. 46 Se quiere insertar el elemento 6. Lo primero es comparar el nuevo elemento con la raíz. Como 6 > 4, entonces la búsqueda prosigue por el lado derecho. Ahora el nuevo nodo se compara con el elemento 8. En este caso 6 < 8, por lo que hay que continuar la búsqueda por la rama izquierda. Como la rama izquierda de 8 no tiene ningún nodo, se cumple la condición de parada de la recursividad y se inserta en ese lugar el nuevo nodo. Borrar El borrado en árboles binarios de búsqueda es otra operación bastante sencilla excepto en un caso. Vamos a ir estudiando los distintos casos. Tras realizar la búsqueda del nodo a eliminar observamos que el nodo no tiene hijos. Este es el caso más sencillo, únicamente habrá que borrar el elemento y ya habremos concuído la operación. Si tras realizar la búsqueda nos encontramos con que tiene un sólo hijo. Este caso también es sencillo, para borrar el nodo deseado, hacemos una especie de puente, el padre del nodo a borrar pasa a apuntar al hijo del nodo borrado.
  • 47. 47 Por último, el caso más complejo, si el nodo a borrar tiene dos hijos. En este caso se debe sustitui el nodo a borrar por mayor de los nomdos menores del nodo borrado, o por el menor de los nodos mayores de dicho nodo. Una vez realizada esta sustitución se borrra el nodo que sustituyó al nodo eliminado (operación sencilla ya que este nodo tendrá un hijo a lo sumo). Sobre el siguiente árbol queremos eliminar el elemento 6. Tenemos dos opciones para sustituirlo: El menor de sus mayores: 7. El mayor de sus menores: 4. Vamos a sustituirlo por el 7 (por ejemplo). El árbol resultante sería el siguiente, tras eliminar también el elemento 7 de su ubicación original.
  • 48. 48 Otras operaciones En los árboles de búsqueda la operación buscar es muy eficiente. El algoritmo compara el elemento a buscar con la raíz, si es menor continua la búsqueda por la rama izquierda, si es mayor continua por la izquierda. Este procedimiento se realiza recursivamente hasta que se encuentra el nodo o hasta que se llega al final del árbol. Otra operación importante en el árbol es el recorridod el mismo. El recorrido se puede realizar de tres formas diferentes: Preorden: Primero el nodo raíz, luego el subárbol izquierdo y a continuación el subárbol derecho. Inorden: Primero el subárbol izquierdo, luego la raíz y a continuación el subárbol derecho. Postorden: Primero el subárbol izquierdo, luego el subárbol derecho y a continuación la raíz. Aplicaciones: Aplicaciones de árboles binarios Un árbol binario es una estructura de datos útil cuando se trata de hacer modelos de procesos en donde se requiere tomar decisiones en uno de dos sentidos en cada parte del proceso. Por ejemplo, supongamos que tenemos un arreglo en donde queremos encontrar todos los duplicados. Esta situación es bastante útil en el manejo de las bases de datos, para evitar un problema que se llama redundancia.
  • 49. Una manera de encontrar los elementos duplicados en un arreglo es recorrer todo 49 el arreglo y comparar con cada uno de los elementos del arreglo. Esto implica que si el arreglo tiene elementos, se deben hacer comparaciones, claro, no es mucho problema si es un número pequeño, pero el problema se va complicando más a medida que aumenta. Si usamos un árbol binario, el número de comparaciones se reduce bastante, veamos cómo. El primer número del arreglo se coloca en la raíz del árbol (como en este ejemplo siempre vamos a trabajar con árboles binarios, simplemente diremos árbol, para referirnos a un árbol binario) con sus subárboles izquierdo y derecho vacíos. Luego, cada elemento del arreglo se compara son la información del nodo raíz y se crean los nuevos hijos con el siguiente criterio: Si el elemento del arreglo es igual que la información del nodo raíz, entonces notificar duplicidad. Si el elemento del arreglo es menor que la información del nodo raíz, entonces se crea un hijo izquierdo. Si el elemento del arreglo es mayor que la información del nodo raíz, entonces se crea un hijo derecho. Una vez que ya está creado el árbol, se pueden buscar los elementos repetidos. Si x el elemento buscado, se debe recorrer el árbol del siguiente modo: Sea k la información del nodo actual p. Si entonces cambiar el nodo actual a right(p), en caso contrario, en caso de que informar una ocurrencia duplicada y en caso de que cambiar el nodo actual a left(p). El siguiente algoritmo leer numero buscado >> n tree=makeTree(n) while(hay numeros en el arreglo){ leeSiguienteNumero >> k p=q=tree; while(k!=info(p)&&q!=NULL){ p=q if(k<info(p)) q=left(p) else q=right(p) } if(k==info(p))
  • 50. despliega<<" el numero es duplicado"; 50 else if (k<info(p)) setLeft(p,k) else setRight(p,k) } Figura 28: Árbol binario para encontrar números duplicados Para saber el contenido de todos los nodos en un árbol es necesario recorrer el árbol. Esto es debido a que solo tenemos conocimiento del contenido de la dirección de un nodo a la vez. Al recorrer el árbol es necesario tener la dirección de cada nodo, no necesariamente todos al mismo tiempo, de hecho normalmente se tiene la dirección de uno o dos nodos a la vez; de manera que cuando se tiene la dirección de un nodo, se dice que se visita ese nodo. Aunque hay un orden preestablecido (la enumeración de los nodos) no siempre es bueno recorrer el árbol en ese orden, porque el manejo de los apuntadores se vuelve más complejo. En su lugar se han adoptado tres criterios principales para recorrer un árbol binario, sin que de omita cualquier otro criterio diferente. Los tres criterios principales para recorrer un árbol binario y visitar todos sus nodos son, recorrer el árbol en: preorden: Se ejecutan las operaciones: 1. Visitar la raíz 2. recorrer el subárbol izquierdo en preorden 3. recorrer el subárbol derecho en preorden entreorden: Se ejecutan las operaciones: 1. recorrer el subárbol izquierdo en entreorden 2. Visitar la raíz
  • 51. 3. recorrer el subárbol derecho en entreorden 51 postorden: Se ejecutan las operaciones: 1. recorrer el subárbol izquierdo en postorden 2. recorrer el subárbol derecho en postorden 3. Visitar la raíz Al considerar el árbol binario que se muestra en la figura 28 usando cada uno de los tres criterios para recorrer el árbol se tienen las siguientes secuencias de nodos: En preorden: En entreorden: En postorden: Esto nos lleva a pensar en otra aplicación, el ordenamiento de los elementos de un arreglo. Para ordenar los elementos de un arreglo en sentido ascendente, se debe construir un árbol similar al árbol binario de búsqueda, pero sin omitir las coincidencias. El arreglo usado para crear el árbol binario de búsqueda fue <14,15,4,9,7,18,3,5,16,4,20,17,9,14,5> El árbol de ordenamiento es el que se muestra en la figura 29
  • 52. Figura 29: Árbol binario para ordenar una secuencia de números 52 Arboles balanceados (avl) Un árbol AVL (llamado así por las iniciales de sus inventores: Adelson-Velskii y Landis) es un árbol binario de búsqueda en el que para cada nodo, las alturas de sus subárboles izquierdo y derecho no difieren en más de 1. No se trata de árboles perfectamente equilibrados, pero sí son lo suficientemente equilibrados como para que su comportamiento sea lo bastante bueno como para usarlos donde los ABB no garantizan tiempos de búsqueda óptimos. El algoritmo para mantener un árbol AVL equilibrado se basa en reequilibrados locales, de modo que no es necesario explorar todo el árbol después de cada inserción o borrado. Operaciones en AVL Los AVL son también ABB, de modo que mantienen todas las operaciones que poseen éstos. Las nuevas operaciones son las de equilibrar el árbol, pero eso se hace como parte de las operaciones de insertado y borrado. Factor de equilibrio Cada nodo, además de la información que se pretende almacenar, debe tener los dos punteros a los árboles derecho e izquierdo, igual que los ABB, y además un miembro nuevo: el factor de equilibrio. El factor de equilibrio es la diferencia entre las alturas del árbol derecho y el izquierdo: FE = altura subárbol derecho - altura subárbol izquierdo; Por definición, para un árbol AVL, este valor debe ser -1, 0 ó 1. Rotación simple a la derecha (SD) Esta rotación se usará cuando el subárbol izquierdo de un nodo sea 2 unidades más alto que el derecho, es decir, cuando su FE sea de -2. Y además, la raíz del subárbol izquierdo tenga una FE de -1 ó 0, es decir, que esté cargado a la izquierda o equilibrado.
  • 53. Grafos: 53 Grafos Desafortunadamente no existe una terminología estandarizada en la teoría de los grafos, por lo tanto es oportuno aclarar que las presentes definiciones pueden variar ligeramente entre diferentes publicaciones de estructura de datos y de teoría de grafos, pero en general se puede decir que un grafo como indica su nombre lo indica es la representación (para nuestro caso) gráfica de los datos de una situación particular, ejemplo: Los datos contienen, en algunos casos, relaciones entre ellos que no es necesariamente jerárquica. Por ejemplo, supongamos que unas líneas aéreas realizan vuelos entre las ciudades conectadas por líneas como se ve en la figura anterior (más adelante se presentaran grafos con estructuras de datos); la estructura de datos que refleja esta relación recibe el nombre de grafo. Se suelen usar muchos nombres al referirnos a los elementos de una estructura de datos. Algunos de ellos son "elemento", "ítem", "asociación de ítems", "registro", "nodo" y "objeto". El nombre que se utiliza depende del tipo de estructura, el contexto en que usamos esa estructura y quien la utiliza. En la mayoría de los textos de estructura de datos se utiliza el termino "registro" al hacer referencia a archivos y "nodo" cuando se usan listas enlazadas, árboles y grafos. También un grafo es una terna G = (V,A,j ), en donde V y A son conjuntos finitos, y j es una aplicación que hace corresponder a cada elemento de A un par de elementos de V. Los elementos de V y de A se llaman, respectivamente, "vértices" y "aristas" de G, y j asocia entonces a cada arista con sus dos vértices. Esta definición da lugar a una representación gráfica, en donde cada vértice es un punto del plano, y cada arista es una línea que une a sus dos vértices. Aristas Son las líneas con las que se unen las aristas de un grafo y con la que se construyen también caminos. Si la arista carece de dirección se denota indistintamente {a, b} o {b, a}, siendo a y b los vértices que une. Si {a ,b} es una arista, a los vértices a y b se les llama sus extremos. Aristas Adyacentes: Se dice que dos aristas son adyacentes si convergen en el mismo vértice.
  • 54. 54 Aristas Paralelas: Se dice que dos aristas son paralelas si vértice inicial y el final son el mismo. Aristas Cíclicas: Arista que parte de un vértice para entrar en el mismo. Cruce: Son dos aristas que cruzan en un punto. Vértices Son los puntos o nodos con los que esta conformado un grafo. Llamaremos grado de un vértice al número de aristas de las que es extremo. Se dice que un vértice es `par' o `impar' según lo sea su grado. Vértices Adyacentes: si tenemos un par de vértices de un grafo (U, V) y si tenemos un arista que los une, entonces U y V son vértices adyacentes y se dice que U es el vértice inicial y V el vértice adyacente. Vértice Aislado: Es un vértice de grado cero. Vértice Terminal: Es un vértice de grado 1. Caminos Sean x, y " V, se dice que hay un camino en G de x a y si existe una sucesión finita no vacía de aristas {x,v1}, {v1,v2},..., {vn,y}. En este caso x e y se llaman los extremos del camino El número de aristas del camino se llama la longitud del camino. Si los vértices no se repiten el camino se dice propio o simple. Si hay un camino no simple entre 2 vértices, también habrá un camino simple entre ellos. Cuando los dos extremos de un camino son iguales, el camino se llama circuito o camino cerrado.
  • 55. Llamaremos ciclo a un circuito simple 55 Un vértice a se dice accesible desde el vértice b si existe un camino entre ellos. Todo vértice es accesible respecto a si mismo Terminología delos grafos Un grafo está formado por un conjunto de nodos(o vértices) y un conjunto de arcos. Cada arco en un grafo se especifica por un par de nodos. El conjunto de nodos es {A, B, C, D, F, G, H} y el conjunto de arcos {(A, B), (A, D), (A,C), (C, D), (C, F), (E, G), (A, A)} para el siguiente grafo Operaciones básicas sobre grafos Suponga que un grafo G se mantiene en memoria mediante la representación enlazada GRAFO (NODO, SIG, ADY, PRINCIPIO, NDISP, ENL, ADISP) Discutida en la sección anterior. Esta sección discute las operaciones, de búsqueda inserción y eliminación de nodos y aristas en el grafo G. La operación de recorrido se trata en la siguiente sección. Procedimiento Busca(INFO, ENL, PRINCIPIO, ITEM, POS) [Algoritmo 5.2]
  • 56. Busca la posición POS del primer nodo que contiene a ITEM, o hace 56 POS : = NULO. 1.-Hacer PTR : =PRINCIPIO. 2.-Repetir mientras PTR sea diferente de NULO: Si ITEM = INFO[PTR], entonces; Hacer POS := PTR y Volver. Si no hacer PTR : = ENL [PTR]. [Fin del Bucle]. 3.-Hacer POS : = NULO y volver. Diferente que en el capítulo 5. Por supuesto, si se usan listas enlazadas circulares a árboles binarios de búsqueda en vez de listas enlazadas, se deben de usar los procedimientos análogos. BÚSQUEDA EN UN GRAFO Suponga que queremos encontrar la posición POS de un nodo N de un grafo C . Esto se puede llevar a cabo mediante el Procedimiento 8.3, tal como sigue: Llamar BUSCA (NODO, SIG, PRINCIPIO, N, POS) O sea, que esta sentencia de llamada busca en la lista NODO el nodo N. Procedimiento 8.4 ELIMINAR (INFO, ENL, PRINCIPIO, DISP, ITEM, INDIC) [Algoritmo 5.10] Elimina el primer nodo de la lista que contenga a ITEM, o hace INDIC:=FALSO si ITEM no aparece en la lista. 1.-[¿Lista vacia?] Si PRINCIPIO = NULO, entonces: Hacer INDIC : = FALSO y volver . 2.-[¿ITEM en primer nodo?] Si INFO [PRINCIPIO] = ITEM, Entonces: Hacer PTR : = PRINCIPIO, PRINCIPIO: =ENL[PRINCIPIO], ENL[PTR]: = DISP, DISP : = PTR, INDIC : = VERDADERO y volver. [Fin del condicional].
  • 57. 3.-Hacer PTR : = ENL[PRINCIPIO] y SALVA : = PRINCIPIO. 57 [Inicializar punteros]. 4.-Repetir pasos 5 y 6 mientras PTR sea diferente a NULO: 5.- Si INFO [PTR] = ITEM entonces: Hacer ENL [SALVA]: = ENL[PTR], ENL[PTR]: = DISP, DISP: = PTR, INDIC: =VERDADERO y volver. [Fin de la condicional]. 6.- Hacer SALVA := PTR y PTR := ENL [PTR]. [Actualizar punteros]. [Fin Bucle del paso 4]. 7.- Hacer INDIC : = FALSO y Volver. INSERCIÓN EN UN GRAFO Suponga que se va a insertar un nodo N en el grafo G. Observe que N será asignado a NODO [NDISP], el primer nodo disponible. Mas aún, como N será un nodo aislado, se debe hacer ADY[NDISP]: = NULO. El procedimiento 8.6 hace esto mediante una variable lógica INDIC que indica si hay desbordamiento. Por supuesto, el Procedimiento 8.6 debe ser modificado si la lista NODO se mantiene como una lista ordenada o como un árbol binario de búsqueda. Procedimiento INSNODO(NODO, SIG, ADY, PRINCIPIO, NDISP, N, INDIC) Este procedimiento inserta el nodo N en le grafo G. 1.-[¿DESBORDAMIENTO?] Si NDISP= NULO, entonces: Hacer INDIC := FALSO y volver. 2.- Hacer ADY[NDISP] : = NULO. 3.-[Quitar el nodo de la lista NDISP]. Hacer NUEVO: =NDISP y NDISP :=SIG[NDISP]. 4.-[Insertar nodo N en la lista NODO]. Hacer NODO[NUEVO] : = N, SIG[NUEVO] :=PRINCIPIO y
  • 58. PRINCIPIO : = NUEVO. 58 5.-Hacer INDIC := VERDADERO y Volver. ELIMINACIÓN DE UN ARCO EN UN GRAFO Suponga que se va a eliminar una arista (A, B) del grafo G. (Nuestro procedimiento asumirá que A y B son nodos de G). De nuevo debemos encontrar primero la posición POSA de A y la posición POSB de B en la lista de nodos. Entonces simplemente eliminamos POSB de la lista de sucesores de A, que tiene el puntero de la lista ADY[POSA].Se usa una variable INDIC lógica para indicar si no existe tal arista en el grafo G. El procedimiento es el siguiente: Procedimiento 8.8 ELIMARISTA(NODO, SIG, ADY, PRINCIPIO, DEST, ENL, ADISP, A, B, INDIC). Este procedimiento elimina la arista (A, B) del grafo G . 1.-Llamar BUSCA(NODO, SIG, PRINCIPIO, A, POSA)[Localizar Nodo A]. 2.- Llamar BUSCA(NODO, SIG, PRINCIPIO, B, POSB) [Localizar Nodo B]. 3.-Llamar ELIMINAR (DEST, ENL, ADY[POSA], ADISP, POSB, INDIC).[Usar el procedimiento 8.4]. 4.-Volver.
  • 59. BIBLIOGRAFIA 59 Técnicas de Diseño de Algoritmos en Java-Escrito por Sonia Jaramillo Valbuena,Sonia Jaramillo Valbuena, Sergio Augusto Cardona Torres, Maria Lili Villegas Ramirez Página 162 Estructuras de datos y algoritmos con Java-Escrito por Adam Drozdek Página 261 Introducción a las Estructuras de datos en Java- Escrito por Sonia Jaramillo Valbuena,Sonia Jaramillo Valbuena, Sergio Augusto Cardona Torres, Dumar Antonio Villa Zapata Página 209
  • 60. Hacer un programa que manipule una lista o una pila o una cola 60 pero que se vea la animación de la operación realizada package Clases; import java.awt.*; import javax.swing.JPanel; public class PanelDibujo extends JPanel { public int x=20; public int y=100; public pila Pila; public boolean swborrar=false; public PanelDibujo(pila p){ Pila=p; } public void paintComponent(Graphics g){ super.paintComponents(g); Graphics2D g2d=(Graphics2D)g; g2d.setColor(Color.WHITE); g2d.fillRect(0,0,400,200);
  • 61. g2d.setColor(Color.BLACK); 61 g2d.setStroke(new BasicStroke(2)); for(int i=0;i<Pila.getNodos().size();i++){ if(i!=0){ g2d.drawLine(2*x*(i+1)-10, y+15, 2*x*(i+1), y+15); } g2d.drawRoundRect(2*x*(i+1), y, 30, 30, 10, 10); g2d.drawString(""+Pila.getNodos().get(i), 2*x*(i+1)+12, y+20); } if(swborrar){ g2d.setColor(Color.WHITE); g2d.fillRect(0,0,400,200); Pila.resetear(true); swborrar=false; } } public boolean isSwborrar() { return swborrar; } public void setSwborrar(boolean swborrar) {
  • 62. this.swborrar = swborrar; 62 } public static void main(String[] args) { } }
  • 63. MÉTODOS DE ORDENAMIENTO 63 Ordenamiento por burbuja package BurbujaPaq; public class Burbujamilenteros { public static void main(String[] args) { int x[]= new int [100]; int i,j, aux; System.out.println("---------NUMEROS GENERADOS-------"); for (i=0; i<x.length; i++) { x[i]=(int)(Math.random()*1000); System.out.println(x[i] + " | "); } System.out.println(""); System.out.println("---------NUMEROS ORDENADOS-------"); for (i=0; i<x.length; i++) { for (j=0; j<x.length-1; j++) { if (x[j] > x[j+1]){ aux=x[j]; x[j]=x[j+1]; x[j+1]=aux; } } } for (i=0; i<x.length; i++) { System.out.println(x[i] + " | "); } } } Hacer un programa en Java que muestre el siguiente menú: 1. Burbuja 2. Shell 3. Quicksort 4. Radix
  • 64. Para cada opción se ordenaran primero 500 elementos y se le 64 toma el tiempo, luego 1000 y se le toma el tiempo y por último 2000 elementos y se le toma el tiempo. Al final imprimir los arreglos originales y su tiempo de ordenamiento. / Crea un archivo de acceso aleatorio, escribiendo 100 registros vacíos en el disco. import java.io.*; import javax.swing.*; public class CrearArchivoAleatorio { private static final int NUMERO_REGISTROS = 100; // permitir al usuario seleccionar el archivo a abrir private void crearArchivo() { // mostrar cuadro de diálogo para que el usuario pueda seleccionar el archivo JFileChooser selectorArchivo = new JFileChooser(); selectorArchivo.setFileSelectionMode( JFileChooser.FILES_ONLY ); int resultado = selectorArchivo.showSaveDialog( null ); // si el usuario hizo clic en el botón Cancelar del cuadro de diálogo, regresar if ( resultado == JFileChooser.CANCEL_OPTION ) return;
  • 65. // obtener el archivo seleccionado 65 File nombreArchivo = selectorArchivo.getSelectedFile(); // mostrar error si el nombre del archivo es inválido if ( nombreArchivo == null || nombreArchivo.getName().equals( "" ) ) JOptionPane.showMessageDialog( null, "Nombre de archivo incorrecto", "Nombre de archivo incorrecto", JOptionPane.ERROR_MESSAGE ); else { // abrir el archivo try { RandomAccessFile archivo = new RandomAccessFile( nombreArchivo, "rw" ); RegistroCuentasAccesoAleatorio registroEnBlanco = new RegistroCuentasAccesoAleatorio(); // escribir 100 registros en blanco for ( int cuenta = 0; cuenta < NUMERO_REGISTROS; cuenta++ ) registroEnBlanco.escribir( archivo ); archivo.close(); // cerrar el archivo // mostrar mensaje indicando que el archivo se creó
  • 66. JOptionPane.showMessageDialog( null, "Se creó el archivo " + 66 nombreArchivo, "Estado", JOptionPane.INFORMATION_MESSAGE ); System.exit( 0 ); // terminar el programa } // fin del bloque try // procesar excepciones durante operaciones de apertura, escritura o cierre del archivo catch ( IOException excepcionES ) { JOptionPane.showMessageDialog( null, "Error al procesar el archivo", "Error al procesar el archivo", JOptionPane.ERROR_MESSAGE ); System.exit( 1 ); } } // fin de instrucción else } // fin del método crearArchivo public static void main( String args[] ) { CrearArchivoAleatorio aplicacion = new CrearArchivoAleatorio(); aplicacion.crearArchivo(); }
  • 67. 67 } // fin de la clase CrearArchivoAleatorio
  • 68. MÉTODOS DE BÚSQUEDA 68 Programa de los hombres búsqueda binaria import java.awt.*; import java.awt.event.*; import java.io.*; import javax.swing.*; public class PruebaFile extends JFrame implements ActionListener { private JTextField campoEntrada; private JTextArea areaSalida; public PruebaFile() { super( "Prueba de la clase File" ); campoEntrada = new JTextField( "Escriba aquí el nombre del archivo o directorio" );
  • 69. campoEntrada.addActionListener( this ); 69 areaSalida = new JTextArea(); ScrollPane panelDesplazable = new ScrollPane(); panelDesplazable.add( areaSalida ); Container contenedor = getContentPane(); contenedor.add( campoEntrada, BorderLayout.NORTH ); contenedor.add( panelDesplazable, BorderLayout.CENTER ); setSize( 400, 400 ); setVisible( true ); } public void actionPerformed( ActionEvent eventoAccion ) { File nombre = new File( eventoAccion.getActionCommand() );
  • 70. if ( nombre.exists() ) { 70 areaSalida.setText( nombre.getName() + " existen" + ( nombre.isFile() ? "es un archivon" : "no es un archivon" ) + ( nombre.isDirectory() ? "es un directorion" : "no es un directorion" ) + ( nombre.isAbsolute() ? "es una ruta absolutan" : "no es una ruta absolutan" ) + "Última modificación: " + nombre.lastModified() + "nLongitud: " + nombre.length() + "nRuta: " + nombre.getPath() + "nRuta absoluta: " + nombre.getAbsolutePath() + "nPadre: " + nombre.getParent() ); if ( nombre.isFile() ) { try { BufferedReader entrada = new BufferedReader( new FileReader( nombre ) ); StringBuffer bufer = new StringBuffer(); String texto; areaSalida.append( "nn" ); while ( ( texto = entrada.readLine() ) != null )
  • 71. bufer.append( texto + "n" ); 71 areaSalida.append( bufer.toString() ); } catch( IOException excepcionES ) { JOptionPane.showMessageDialog( this, "ERROR EN ARCHIVO", "ERROR EN ARCHIVO", JOptionPane.ERROR_MESSAGE ); } } else if ( nombre.isDirectory() ) { String directorio[] = nombre.list(); areaSalida.append( "nnContenido del directorio:n"); for ( int i = 0; i < directorio.length; i++ ) areaSalida.append( directorio[ i ] + "n" ); }
  • 72. 72 } else { JOptionPane.showMessageDialog( this, eventoAccion.getActionCommand() + " no existe", "ERROR", JOptionPane.ERROR_MESSAGE ); } } public static void main( String args[] ) { PruebaFile aplicacion = new PruebaFile(); aplicacion.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); } } Programa de las mujeres búsqueda secuencial import java.util.*;
  • 73. 73 class programa8 { public static void main(String[]args) { int a[],n,n1,indice; Scanner sc=new Scanner(System.in); System.out.print("Ingresa tamaño del vector: "); n=sc.nextInt(); a=new int[n]; a=inicializa(n); muestra(a); System.out.print("Ingresa numero a buscar: "); n1=sc.nextInt(); indice=busquedaLineal(a,n1); if(indice==-1)
  • 74. { 74 System.out.println("tu número no esta en la lista"); } else { System.out.println("tu número esta en el indice: "+indice); } } static int[] inicializa(int n) { int i,j,a[]=new int[n]; for(i=0;i<n;i++) { a[i]=randomxy(1,50); } return a; } static int busquedaLineal(int a[],int n) { int i;
  • 75. for (i=0;i<a.length;i++) 75 { if (a[i] == n) { return i+1; } } return -1; } static void muestra(int a[]) { int n=a.length; for(int i=0;i<n;i++) { System.out.print(a[i]+" "); } System.out.print("nn"); } static int randomxy(int x,int y) {
  • 76. int ran=(int) (Math.floor(Math.random()*(y-x+1))+x); 76 return ran; } } Programa De Examen import java.util.*; class busqueda { public static void main(String[]args) { int a[],n,n1,indice; Scanner sc=new Scanner(System.in); System.out.print("Ingresa tamaño del arreglo: "); n=sc.nextInt(); a=new int[n]; a=inicializa(n);
  • 77. muestra(a); 77 System.out.print("Ingresa numero a buscar: "); n1=sc.nextInt(); indice=busqueda1(a,n1); if(indice==-1) { System.out.println("tu número no esta en la lista"); } else { System.out.println("tu número esta en el indice: "+indice); } } static int[] inicializa(int n) { int i,j,a[]=new int[n]; for(i=0;i<n;i++) { a[i]=randomxy(1,50); }
  • 78. return a; 78 } static int busqueda1(int a[],int n) { int i; for (i=0;i<a.length;i++) { if (a[i] == n) { return i+1; } } return -1; } static void muestra(int a[]) { int n=a.length; for(int i=0;i<n;i++) { System.out.print(a[i]+" "); } System.out.print("nn");
  • 79. } 79 static int randomxy(int x,int y) { int ran=(int) (Math.floor(Math.random()*(y-x+1))+x); return ran; } } ANÁLISIS DE LOS ALGORITMOS
  • 80. 80 INTRODUCCIÓN Los algoritmos se pueden clasificarse según la complejidades de tiempo y espacio, y a este respecto pueden distinguirse varias clases de algoritmos, como lo ilustran la siguiente figura. Su crecimiento también se muestra, por ejemplo, los algoritmo se llama constante si su tiempo de ejecución sigue siendo el mismo para cualquier número de elementos; se llama cuadrático si su tiempo de ejecución es O (n2).para cada una de estas clases de algoritmos y sus tiempo de ejecución en una computadora que realiza un millón de operaciones por segundo (1s=106µs=103ms). Clase complejidad 10 102 103 n constante O(1) 1 1µs 1 1µs 1 1µs Logarítmica O(1gn) 3.32 3µs 6.64 7µs 9.97 10µs Lineal O(n) 10 10µs 102 100µs 103 1ms O(n1gn) O(n1gn) 33.2 33µs 664 664µs 9970 10ms Cuadrática O(n2) 102 100µs 104 10ms 106 1s ANÁLISIS DE LOS ALGORITMOS.
  • 81. 81 Es una disciplina de las ciencias de la computación y, en la mayoría de los casos, su estudio es completamente abstracto sin usar ningún tipo de lenguaje de programación ni cualquier otra implementación. Así, el análisis de los algoritmos se centra en los principios básicos del algoritmo, no en los de la implementación particular. Una forma de plasmar (o algunas veces "codificar") un algoritmo es escribirlo en pseudocódigo o utilizar un lenguaje muy simple tal como Léxico, cuyos códigos pueden estar en el idioma del programador. Por ejemplo: Un algoritmo que verifica que hay más ceros que unos en una secuencia binaria infinita debe ejecutarse siempre para que pueda devolver un valor útil. Si se implementa correctamente, el valor devuelto por el algoritmo será válido, hasta que evalúe el siguiente dígito binario. De esta forma, mientras evalúa la siguiente secuencia podrán leerse dos tipos de señales: una señal positiva (en el caso de que el número de ceros sea mayor que el de unos) y una negativa en caso contrario. Finalmente, la salida de este algoritmo se define como la devolución de valores exclusivamente positivos si hay más ceros que unos en la secuencia y, en cualquier otro caso, devolverá una mezcla de señales positivas y negativas. COMPLEJIDAD EN EL TIEMPO Y ESPACIO
  • 82. 82 Complejidad en el tiempo:Cantidad de tiempo necesario para la ejecución Órdenes de Complejidad Se dice que O (f(n)) define un "orden de complejidad". Escogeremos como representante de este orden a la función f(n) más sencilla del mismo. Así tendremos O(1) orden constante O(log n) orden logarítmico O(n) orden lineal O(n log n) O(n2) orden cuadrático O(na) orden polinomiol (a > 2) O(an) orden exponencial (a > 2) O (n!) orden factorial Es más, se puede identificar una jerarquía de órdenes de complejidad que coincide con el orden de la tabla anterior; jerarquía en el sentido de que cada orden de complejidad superior tiene a los inferiores como subconjuntos. Si un algoritmo A se puede demostrar de un cierto orden O1, es cierto que también pertenece a todos los órdenes superiores (la relación de orden cota superior de' es transitiva); pero en la práctica lo útil es encontrar la "menor cota superior", es decir el menor orden de complejidad que lo cubra. Los algoritmos de complejidad O(n) y O(n log n) son los que muestran un comportamiento más "natural": prácticamente a doble de tiempo, doble de datos procesables. Los algoritmos de complejidad logarítmica son un descubrimiento fenomenal, pues en el doble de tiempo permiten atacar problemas notablemente mayores, y para resolver un problema el doble de grande sólo hace falta un poco más de tiempo (ni mucho menos el doble).
  • 83. Los algoritmos de tipo polinómico no son una maravilla, y se enfrentan con dificultad a 83 problemas de tamaño creciente. La práctica viene a decirnos que son el límite de lo "tratable". Sobre la tratabilidad de los algoritmos de complejidad polinómicahabria mucho que hablar, y a veces semejante calificativo es puro eufemismo. Mientras complejidades del orden O (n2) y O (n3) suelen ser efectivamente abordables, prácticamente nadie acepta algoritmos de orden O(n100), por muy polinómicos que sean. La frontera es imprecisa. Cualquier algoritmo por encima de una complejidad polinómico se dice "intratable" y sólo será aplicable a problemas ridículamente pequeños. Por ejemplo, si disponemos de dos algoritmos para el mismo problema, con tiempos de ejecución respectivos: algoritmo tiempo complejidad f 100 n O(n) 2 g n O(n2) Asintóticamente, "f" es mejor algoritmo que "g"; pero esto es cierto a partir de N > 100.Si nuestro problema no va a tratar jamás problemas de tamaño mayor que 100, es mejor solución usar el algoritmo "g". El ejemplo anterior muestra que las constantes que aparecen en las fórmulas para T(n), y que desaparecen al calcularlas funciones de complejidad, pueden ser decisivas desde el punto de vista de ingeniería. Pueden darse incluso ejemplo: algoritmo tiempo complejidad f 100 n O(n) g n2 O(n2)
  • 84. 84 Las siguientes propiedades se pueden utilizar como reglas para el cálculo de órdenes de complejidad. Toda la maquinaria matemática para el cálculo de límites se puede aplicar directamente: C. Lim (n->inf) f (n)/g (n) = 0 => f IN O (g ) =>g NOT_IN O(f)=>O(f) es subconjunto de O(g) D. Lim (n->inf) f (n)/g (n) = k => f IN O(g) =>g IN O(f) => O(f) = O(g) E. Lim(n->inf)f(n)/g(n)= INF => f NOT_IN O(g) =>g IN O(f) =>O(f) es superconjunto de O(g) Las que siguen son reglas habituales en el cálculo de límites: F. Si f, g IN O(h) =>f+g IN O(h) G. Sea k una constante, f(n) IN O(g) => k*f(n) IN O(g) H. Si f IN O(h1) y g IN O(h2) =>f+g IN O(h1+h2) I. Si f IN O(h1) y g IN O(h2) => f*g IN O(h1*h2) J. Sean los reales 0 < a < b =>O(na) es subconjunto de O(nb) K. Sea P(n) un polinomio de grado k => P(n) IN O(nk) L. Sean los reales a, b > 1 =>O(loga) = O(logb) La regla [L] nos permite olvidar la base en la que se calculan los logaritmos en expresiones de complejidad. La combinación de las reglas [K, G] es probablemente la más usada, permitiendo de un plumazo olvidar todos los componentes de un polinomio, menos su grado. Por último, la regla [H] es la basica para analizar el concepto de secuencia en un programa: la composición secuencial de dos trozos de programa es de orden de complejidad el de la suma de sus partes.
  • 85. Costes en tiempo y en espacio 85 La característica básica que debe tener un algoritmo es que sea correcto, es decir, que produzca el resultado deseado en tiempo finito. Adicionalmente puede interesarnos que sea claro, que este bien estructurado, que sea fácil de usar, que sea fácil de implementar y que sea eficiente. Entendemos por eficiencia de un algoritmo la cantidad de recursos de computo que requiere; es decir, cual es su tiempo de ejecución que cantidad de memoria utiliza. A la cantidad de tiempo que requiere la ejecución de un cierto algoritmo se le suele llamar coste en tiempo mientras que a la cantidad de memoria que requiere se le suele llamar coste en espacio. Es evidente que conviene buscar algoritmos correctos que mantengan tan bajo como sea posible el consumo de recursos que hacen del sistema, es decir, que sean lo mas eficientes posible. Cabe hacer notar que el concepto de eficiencia de un algoritmo es un concepto relativo, esto quiere decir que ante dos algoritmos correctos que resuelven el mismo problema, uno es mas eficiente que otro si consume menos recursos. Por tanto, podemos observar que el concepto de eficiencia y en consecuencia el concepto de coste nos permitirá comparar distintos algoritmos entre ellos. Si consideramos los algoritmos elementales de ordenación por selección y por Inserción ¿Cómo podríamos elegir cual de ellos utilizar en una aplicación dada?}Veremos más adelante que para efectos prácticos ambos algoritmos son similares y que son eficientes solo para ordenar secuencias con un número pequeño de elementos. Sin embargo hay varias comparaciones que se pueden hacer entre ellos. Tamaño del vector Núm. Comparaciones Núm. Intercambios Selección n(n − 1)/2 0 (min.) n− 1 (max.) n− 1 (min.) 0 (min.) Insertion prop.n2/4 (prom.) prop. N2/4 (prom.) Prop. N2/2 (max.) prop. N2/2 De la tabla anterior podemos inferir que para ordenar secuencias cuyos elementos Sean difíciles de comparar (las comparaciones son caras) es más conveniente Utilizar el método de ordenación por inserción, y que este método es más eficiente mientras
  • 86. más ordenada este la secuencia de entrada. Si por el contrario queremos ordenar 86 secuencias de elementos que son fáciles de comparar (o que tienen claves asociadas fáciles de comparar) y en cambio los intercambios son carosseria más conveniente utilizar el algoritmo de ordenación por selección. En general, ¿cómo podemos elegir entre un algoritmo y otro si ambos resuelven el mismo problema? Pero este método tiene varios inconvenientes ya que los resultados dependen: del subconjunto de pruebas escogidas del ordenador en el que se realicen las pruebas del lenguaje de programación utilizado o del compilador, entre otros factores. Ejemplo. Análisis de la funciona os min, que encuentra la posición del elemento mínimo en un vector de enteros. El coste en tiempo de un algoritmo depende del tipo de operaciones que realiza y del coste especifico de estas operaciones. Este coste suele calcularse únicamente en función del tamaño de las entradas para que sea independiente del tipo de maquina, lenguaje de programación, compilador, etc. en que se implementa el algoritmo. Definición 1.1 Dado un algoritmo A cuyo conjunto de entradas es A, su eficiencia o coste (en tiempo, en espacio, en número de operaciones de entrada/ salida, etc.) es una función tal que: T: A! R+ ∞ 7! T (∞) COMPARACION DE COMPLEJIDAD Y TIEMPO EN ALGORITMOS. Se analizara el tiempo de ejecución y el tiempo empleado por los algoritmos de inserción directa, selección directa, el método de la burbuja y el método de la burbuja mejorado, ordenando una arreglo de enteros en 3 casos: en orden ascendente (mejor caso), un orden descendente (peor caso) y un orden al azar (caso promedio). ALGORITMOS: - INSERCION DIRECTA: Para x=2 hasta n (+)
  • 87. Y x-1 87 Sw 0 Mientras (y<>0) y (sw=0) Si A[y-1]>A[y] AuxA[y-1] A[y-1]A[y] A[y]Aux De lo contrario Sw1 Yy-1 SELECCIÓN DIRECTA: Para x=1 hasta n-1 (+) Menorx Para y=x+1 hasta n (+) Si A[y] <A [menor] Menory Si menor<>x AuxA[x] A[x] A[menor]
  • 88. A[menor] Aux 88 - METODO DE LA BURBUJA: Para i=1 hasta n-1 Para j=i+1 hasta n Si A[i]>A[j] AuxA[i] A[i]A[j] A[j]Aux METODO DE LA BURBUJA MEJORADO: Sw1 Mientras sw=1 Sw 0 Para i=1 hasta n-1 (+) Si A[i] >A[i+1] AuxA[i] A[i]A[i+1] A[i+1]Aux
  • 89. COMPARACION DE COMPLEJIDADES: 89 ORDEN DE COMPLEJIDAD INSERCION SELECCION BURBUJA BURBUJA DIRECTA DIRECTA (MEJORADO) MEJOR O(N) O(N2) O(N2) O(N) CASO CASO O(N2) O(N2) O(N2) O(N2) PROMEDIO PEOR CASO O(N2) O(N2) O(N2) O(N log N) TIEMPO EN COMPARACIONES Y ASIGNACIONES: Probar que son equivalentes las condiciones siguientes: 1. G es un grafo bipartito. 2. G es un grafo 2{coloreable. 3. G es un grafo que carece de ciclos de longitud impar. 1. COMPLEJIDAD ALGORÍTMICA Un algoritmo será mas eficiente comparado con otro, siempre que consuma menos recursos, como el tiempo y espacio de memoria necesarios para ejecutarlo. La eficiencia de un algoritmo puede ser cuantificada con las siguientes medidas de complejidad: 1. Complejidad Temporal o Tiempo de ejecución: Tiempo de cómputo necesario para ejecutar algún programa. 2. Complejidad Espacial: Memoria que utiliza un programa para su ejecución, La eficiencia en memoria de un algoritmo indica la cantidad de espacio requerido para ejecutar el algoritmo; es decir, el espacio en memoria que ocupan todas las variables
  • 90. propias al algoritmo. Para calcular la memoria estática de un algoritmo se suma la 90 memoria que ocupan las variables declaradas en el algoritmo. Para el caso de la memoria dinámica, el cálculo no es tan simple ya que, este depende de cada ejecución del algoritmo. Este análisis se basa en las Complejidades Temporales, con este fin, para cada problema determinaremos una medida N, que llamaremos tamaño de la entrada o número de datos a procesar por el programa, intentaremos hallar respuestas en función de dicha N.El concepto exacto que cuantifica N dependerá de la naturaleza del problema, si hablamos de un array se puede ver a N como el rango del array, para una matriz, el número de elementos que la componen; para un grafo, podría ser el número de nodos o arcos que lo arman, no se puede establecer una regla para N, pues cada problema acarrea su propia lógica y complejidad. Tiempo de Ejecución: El tiempo de Ejecución de un programa se mide en función de N, lo que designaremos como (N).Esta función se puede calcular físicamente ejecutando el programa acompañados de un reloj, o calcularse directamente sobre el código, contando las instrucciones a ser ejecutadas y multiplicando por el tiempo requerido por cada instrucción. Así, un trozo sencillo de código como: S1; for(x = 0; x < N; x++) S2; Demanda: T(N) = t1 + t2 * N Donde t1 es el tiempo que lleva ejecutar la serie S1 de sentencias, y t2 es el que lleva la serie S2.Habitualmente todos los algoritmos contienen alguna sentencia condicional o selectiva, haciendo que las sentencias ejecutadas dependan de la condición lógica, esto hace que aparezca más de un valor para T(N), es por ello que debemos hablar de un rango de valores: Tmin(N) ≤ T(N) ≤ Tmax(N)
  • 91. Estos extremos son llamados "el peor caso" y "el mejor caso" y entre ambos se puede 91 hallar "el caso promedio" o el más frecuente, siendo este el más difícil de estudiar; nos centraremos en el "el peor caso" por ser de fácil cálculo y se acerca a "el caso promedio", brindándonos una medida pesimista pero fiable.Toda función T(N) encierra referencias al parámetro N, y a una serie de constantes Ti dependientes de factores externos al algoritmo. Se tratará de analizar los algoritmos dándoles autonomía frente a estos factores externos, buscando estimaciones generales ampliamente válidas, a pesar de ser demostraciones teóricas. COMPLEJIDAD EN ESPACIO Cantidad de memoria necesaria para la ejecución La misma idea que se utiliza para medir la complejidad en tiempo de un algoritmo se utiliza para medir su complejidad en espacio. Decir que un programa es O( N ) en espacio significa que sus requerimientos de memoria aumentan proporcionalmente con el tamaño del problema. Esto es, si el problema se duplica, se necesita el doble de memoria. Del mismo modo, para un programa de complejidad O( N2 ) en espacio, la cantidad de memoria que se necesita para almacenar los datos crece con el cuadrado del tamaño del problema: si el problema se duplica, se requiere cuatro veces más memoria. En general, el cálculo de la complejidad en espacio de un algoritmo es un proceso sencillo que se realiza mediante el estudio de las estructuras de datos y su relación con el tamaño del problema. El problema de eficiencia de un programa se puede plantear como un compromiso entre el tiempo y el espacio utilizados. En general, al aumentar el espacio utilizado para almacenar la información, se puede conseguir un mejor desempeño, y, entre más compactas sean las estructuras de datos, menos veloces resultan los algoritmos. Lo mismo sucede con el tipo de estructura de datos que utilice un programa, puesto que cada una de ellas lleva implícitas unas limitaciones de eficiencia para sus operaciones básicas de administración. Por eso, la etapa de diseño es tan importante dentro del proceso de construcción de software, ya que va a determinar en muchos aspectos la calidad del producto obtenido.
  • 92. SELECCIÓN DE UN ALGORITMO 92 La escogencia de un algoritmo para resolver un problema es un proceso en el que se deben tener en cuenta muchos factores, entre los cuales se pueden nombrar los siguientes: La complejidad en tiempo del algoritmo. Es una primera medida de la calidad de una rutina, y establece su comportamiento cuando el número de datos que debe procesar es muy grande. Es importante tenerla en cuenta, pero no es el único factor que se debe considerar. La complejidad en espacio del algoritmo. Es una medida de la cantidad de espacio que necesita la rutina para representar la información. Sólo cuando esta complejidad resulta razonable es posible utilizar este algoritmo con seguridad. Si las necesidades de memoria crecen desmesuradamente con respecto al tamaño del problema, el rango de utilidad del algoritmo es bajo y se debe descartar. La dificultad de implementar el algoritmo. En algunos casos el algoritmo óptimo puede resultar tan difícil de implementar, que no se justifique desarrollarlo para la aplicación que se le va a dar a la rutina. Si su uso es bajo o no es una operación crítica del programa que se está escribiendo, puede resultar mejor adoptar un algoritmo sencillo y fácil de implementar, aunque no sea el mejor de todos.
  • 93. 93 EFICIENCIA DE LOS ALGORITMOS La eficiencia de un algoritmo, se suelen estudiar los recursos (memoria y tiempo) que consume el algoritmo. El análisis de algoritmos se ha desarrollado para obtener valores que de alguna forma indiquen (o especifiquen) la evolución del gasto de tiempo y memoria en función del tamaño de los valores de entrada.