miércoles, 28 de noviembre de 2012

UNIDAD III Grafos y Arboles


Resumen


GRAFOS

 

Un grafo en el ámbito de las ciencias de la computación es una estructura de datos, en concreto un tipo abstracto de datos (TAD), que consiste en un conjunto de nodos (también llamados vértices) y un conjunto de arcos (aristas) que establecen relaciones entre los nodos. El concepto de grafo TAD desciende directamente del concepto matemático de grafo.

Informalmente se define como G = (V, E), siendo los elementos de V los vértices, y los elementos de E, las aristas (edges en inglés). Formalmente, un grafo, G, se define como un par ordenado, G = (V, E), donde V es un conjunto finito y E es un conjunto que consta de dos elementos de V.

Formas de representación

Existen diferentes implementaciones del tipo grafo: con una matriz de adyacencias (forma acotada) y con listas y metalistas de adyacencia (no acotadas).

  • Matriz de adyacencias: se asocia cada fila y cada columna a cada nodo del grafo, siendo los elementos de la matriz la relación entre los mismos, tomando los valores de 1 si existe la arista y 0 en caso contrario.
 


  • Lista de adyacencias: se asocia a cada nodo del grafo una lista que contenga todos aquellos nodos que sean adyacentes a él.



Especificación de los tipos abstractos de datos de un grafo no dirigido


GENERADORES


Crear un grafo vacío: Devuelve un grafo vacío.

  • op crearGrafo : -> Grafo [ctor] .

Añadir una arista: Dado un grafo, añade una relación entre dos nodos de dicho grafo.

  • op añadirArista : Grafo Nodo Nodo -> [Grafo] [ctor] .

Añadir un nodo: Dado un grafo, incluye un nodo en el en caso en el que no exista previamente.

  • op añadirNodo : Grafo Nodo -> Grafo [ctor] .

CONSTRUCTORES


Borrar nodo: Devuelve un grafo sin un nodo y las aristas relacionadas con él. Si dicho nodo no existe se devuelve el grafo inicial.

  • op borrarNodo : Grafo Nodo -> Grafo .

Borrar arista: Devuelve un grafo sin la arista indicada. En caso de que la arista no exista devuelve el grafo inicial.

  • op borrarArista : Grafo Nodo Nodo -> Grafo .

SELECTORES


Grafo Vacio: Comprueba si un grafo no tiene ningún nodo.

  • op esVacio : Grafo -> Bool .

Contener Nodo: Comprueba si un nodo pertenece a un grafo.

  • op contiene: Grafo Nodo -> Bool .

Adyacentes: Comprueba si dos nodos tienen una arista que los relacione.

  • op adyacentes: Grafo Nodo Nodo -> Bool .

Para la especificación de un grafo dirigido tenemos que modificar algunas de las ecuaciones de las operaciones borrar Arista y añadir Arista para que no se considere el caso de aristas bidireccionales.

Y sustituir la operación adyacente por:

  • op predecesor: Grafo Nodo Nodo -> Bool .
  • op sucesor: Grafo Nodo Nodo -> Bool .

Segunda  Definición de Grafos

 

El origen de la palabra grafo es griego y su significado etimológico es "trazar". Aparece con gran frecuencia como respuesta a problemas de la vida cotidiana, algunos ejemplos podrían ser los siguientes: un gráfico de una serie de tareas a realizar indicando su secuenciación (un organigrama),grafos matemáticos que representan las relaciones binarias, una red de carreteras, la red de enlaces ferroviarios o aéreos o la red eléctrica de una ciudad.(Véase la figura 1).En cada caso, es conveniente representar gráficamente el problema dibujando un grafo como un conjunto de puntos(vértices)con líneas conectándolos (arcos).
 
 


 

De aquí se podría deducir que un grafo es básicamente un objeto geométrico aunque en realidad sea un objeto combinatorio, es decir, un conjunto de puntos y un conjunto de líneas tomado de entre el conjunto de líneas que une cada par de vértices. Por otro lado, y debido a su generalidad y a la gran diversidad de formas que pueden usarse, resulta complejo tratar con todas las ideas relacionadas con un grafo.

Los grafos son estructuras de datos no lineales que tienen una naturaleza generalmente dinámica. Su estudio podría dividirse en dos grandes bloques:

  • Grafos Dirigidos.
  • Grafos no Dirigidos (pueden ser considerados un caso particular de los anteriores).

Un ejemplo de grafo dirigido lo constituye la red de aguas de una ciudad ya que cada tubería sólo admite que el agua la recorra en un único sentido. Por el contrario, la red de carreteras de un país representa en general un grafo no dirigido, puesto que una misma carretera puede ser recorrida en ambos sentidos. No obstante, podemos dar unas definiciones generales para ambos tipos.

A continuación daremos definiciones de los dos tipos de grafos y de los conceptos que llevan asociados.

no dirigido.

 

 

 

 

 

EJEMPLO:










Estructuras Internas del TDA grafo.


 

/* Implementacion basada en una lista de nodos de los que cuelga */
/* la lista de arcos de salida.                                  */
 
#include 
#include 
#include 
 
#define TE 5
#define Nulo NULL
 
typedef char  *tetq;
typedef float tvalor;
 
typedef struct arco {
               struct nodo *origen;
               struct nodo *destino;
               tvalor valor;
               struct arco *sig;
} *tarco;
 
typedef struct nodo {
               int nodo;
               tetq etiq;
               tarco ady;
               tarco inc;
               struct nodo *sig;
} *tnodo;
 
typedef tnodo tgrafo;

 

IMPLEMENTACIÓN DE LAS PRIMITIVAS.


 

tgrafo Crear(void)
{
   tnodo aux;
   
   aux = (tnodo)malloc(sizeof(struct nodo));
   if (aux == NULL) {
      error(\"Error en Crear.\");
   } else {
      aux->nodo = 0;
                 aux->etiq = NULL;
                 aux->ady  = NULL;
                 aux->inc  = NULL;
                 aux->sig  = NULL;
                 return aux;
   }
}

void BorrarArco(tnodo org, tnodo dest, tgrafo g)

{

   tarco a,ant;

   int enc=0;

  

   if (org->ady==NULL) return;

   else if (org->ady->destino==dest) {

                 a = org->ady;

                 org->ady = a->sig;

                 free(a);

   }

   else {

      ant = org->ady;

      a = ant->sig;

      while (!enc && (a!=NULL)) {

                    if (a->destino==dest) enc=1;

         else {

            a = a->sig;

                                              ant = ant->sig;

         }

      }

      if (a==NULL) return;

      else {

         ant->sig = a->sig;

         free(a);

      }                       

   }

 

   enc=0;

   if (dest->inc==NULL) return;

   else if (dest->inc->origen==org) {

     a = dest->inc;

                dest->inc = a->sig;

     free(a);              

   }

   else {

     ant = dest->inc;

     a = ant->sig;

     while (!enc && (a!=NULL)) {

       if (a->origen == org) enc=1;

                  else {

         a = a->sig;

                                ant = ant->sig;

       }      

     }

     if (a==NULL) return;

     else {

       ant->sig = a->sig;

       free(a);

                }

   }

}

 

void Destruir(tgrafo G)

{

   tnodo n;

   tarco a_aux;

 

   while (g->sig != NULL) {

      n = g->sig;

                 while (n->ady         != NULL) {

                               a_aux = n->ady;

                               n->ady = a_aux->sig;

        free(a_aux);

      }

                 while (n->inc != NULL) {

         a_aux = n->inc;

                                n->inc = a_aux->sig;

         free(a_aux);  

                 }                           

                 g->sig = n->sig;

      free(n->etiq);

      free(n);

   }          

   free(g);

}

 

Un grafo dirigido G consiste en un conjunto de vértices V y un conjunto de arcos o aristas A. Los vertice se denominan también nodos o puntos.

Un arco, es un par ordenado de vértices(V,W) donde V es el vértice inicial y W es el vértice terminal del arco. Un arco se expresa como: V–>W

Los vértice de un grafo pueden usarse para representar objetos. Los arcos se utilizan para representar relaciones entre estos objetos.

Las aplicaciones más importantes de los grafos son las siguientes:

Rutas entre ciudades.

Determinar tiempos máximos y mínimos en un proceso.

Flujo y control en un programa.

 

 

ARBOLES

 

Un árbol es una estructura no lineal en la que cada nodo puede apuntar a uno o varios nodos.

También se suele dar una definición recursiva: un árbol es una estructura en compuesta por un dato y varios árboles.

Esto son definiciones simples. Pero las características que implican no lo son tanto.


Árbol

Definiremos varios conceptos. En relación con otros nodos:

  • Nodo hijo: cualquiera de los nodos apuntados por uno de los nodos del árbol. En el ejemplo, 'L' y 'M' son hijos de 'G'.
  • Nodo padre: nodo que contiene un puntero al nodo actual. En el ejemplo, el nodo 'A' es padre de 'B', 'C' y 'D'.

Los árboles con los que trabajaremos tienen otra característica importante: cada nodo sólo puede ser apuntado por otro nodo, es decir, cada nodo sólo tendrá un padre. Esto hace que estos árboles estén fuertemente jerarquizados, y es lo que en realidad les da la apariencia de árboles.

En cuanto a la posición dentro del árbol:

  • Nodo raíz: nodo que no tiene padre. Este es el nodo que usaremos para referirnos al árbol. En el ejemplo, ese nodo es el 'A'.
  • Nodo hoja: nodo que no tiene hijos. En el ejemplo hay varios: 'F', 'H', 'I', 'K', 'L', 'M', 'N' y 'O'.
  • Nodo rama: aunque esta definición apenas la usaremos, estos son los nodos que no pertenecen a ninguna de las dos categorías anteriores. En el ejemplo: 'B', 'C', 'D', 'E', 'G' y 'J'.

Otra característica que normalmente tendrán nuestros árboles es que todos los nodos contengan el mismo número de punteros, es decir, usaremos la misma estructura para todos los nodos del árbol. Esto hace que la estructura sea más sencilla, y por lo tanto también los programas para trabajar con ellos.

Tampoco es necesario que todos los nodos hijos de un nodo concreto existan. Es decir, que pueden usarse todos, algunos o ninguno de los punteros de cada nodo.

Un árbol en el que en cada nodo o bien todos o ninguno de los hijos existe, se llama árbol completo.

En una cosa, los árboles se parecen al resto de las estructuras que hemos visto: dado un nodo cualquiera de la estructura, podemos considerarlo como una estructura independiente. Es decir, un nodo cualquiera puede ser considerado como la raíz de un árbol completo.

Existen otros conceptos que definen las características del árbol, en relación a su tamaño:

  • Orden: es el número potencial de hijos que puede tener cada elemento de árbol. De este modo, diremos que un árbol en el que cada nodo puede apuntar a otros dos es de orden dos, si puede apuntar a tres será de orden tres, etc.
  • Grado: el número de hijos que tiene el elemento con más hijos dentro del árbol. En el árbol del ejemplo, el grado es tres, ya que tanto 'A' como 'D' tienen tres hijos, y no existen elementos con más de tres hijos.
  • Nivel: se define para cada elemento del árbol como la distancia a la raíz, medida en nodos. El nivel de la raíz es cero y el de sus hijos uno. Así sucesivamente. En el ejemplo, el nodo 'D' tiene nivel 1, el nodo 'G' tiene nivel 2, y el nodo 'N', nivel 3.
  • Altura: la altura de un árbol se define como el nivel del nodo de mayor nivel. Como cada nodo de un árbol puede considerarse a su vez como la raíz de un árbol, también podemos hablar de altura de ramas. El árbol del ejemplo tiene altura 3, la rama 'B' tiene altura 2, la rama 'G' tiene altura 1, la 'H' cero, etc.

Los árboles de orden dos son bastante especiales, de hecho les dedicaremos varios capítulos. Estos árboles se conocen también como árboles binarios.

Frecuentemente, aunque tampoco es estrictamente necesario, para hacer más fácil moverse a través del árbol, añadiremos un puntero a cada nodo que apunte al nodo padre. De este modo podremos avanzar en dirección a la raíz, y no sólo hacia las hojas.

Es importante conservar siempre el nodo raíz ya que es el nodo a partir del cual se desarrolla el árbol, si perdemos este nodo, perderemos el acceso a todo el árbol.

El nodo típico de un árbol difiere de los nodos que hemos visto hasta ahora para listas, aunque sólo en el número de nodos. Veamos un ejemplo de nodo para crear árboles de orden tres:

struct nodo \{

   int dato;

   struct nodo *rama1;

   struct nodo *rama2;

   struct nodo *rama3;

};

O generalizando más:

#define ORDEN 5

 

struct nodo \{

   int dato;

   struct nodo *rama[ORDEN];

};

Para C, y basándonos en la declaración de nodo que hemos visto más arriba, trabajaremos con los siguientes tipos:

typedef struct _nodo \{

   int dato;

   struct _nodo *rama[ORDEN];

} tipoNodo;

 

typedef tipoNodo *pNodo;

typedef tipoNodo *Arbol;

Al igual que hicimos con las listas que hemos visto hasta ahora, declaramos un tipo tipoNodo para declarar nodos, y un tipo pNodo para es el tipo para declarar punteros a un nodo.

Arbol es el tipo para declarar árboles de orden ORDEN.

 

 

Segunda de Definición de Arboles

 

En ciencias de la informática, un árbol es una estructura de datos ampliamente usada que imita la forma de un árbol (un conjunto de nodos conectados). Un nodo es la unidad sobre la que se construye el árbol y puede tener cero o más nodos hijos conectados a él. Se dice que un nodo es padre de un nodo si existe un enlace desde hasta (en ese caso, también decimos que es hijo de ). Sólo puede haber un único nodo sin padres, que llamaremos raíz. Un nodo que no tiene hijos se conoce como hoja. Los demás nodos (tienen padre y uno o varios hijos) se les conoce como rama.

Formalmente, podemos definir un árbol de la siguiente forma:

  • Caso base: un árbol con sólo un nodo (es a la vez raíz del árbol y hoja).

  • Un nuevo árbol a partir de un nodo y árboles de raíces con elementos cada uno, puede construirse estableciendo una relación padre-hijo entre y cada una de las raíces de los árboles. El árbol resultante de nodos tiene como raíz el nodo , los nodos son los hijos de y el conjunto de nodos hoja está formado por la unión de los conjuntos hojas iniciales. A cada uno de los árboles se les denota ahora subárboles de la raíz.

Una sucesión de nodos del árbol, de forma que entre cada dos nodos consecutivos de la sucesión haya una relación de parentesco, decimos que es un recorrido árbol. Existen dos recorridos típicos para listar los nodos de un árbol: primero en profundidad y primero en anchura. En el primer caso, se listan los nodos expandiendo el hijo actual de cada nodo hasta llegar a una hoja, donde se vuelve al nodo anterior probando por el siguiente hijo y así sucesivamente. En el segundo, por su parte, antes de listar los nodos de nivel (a distancia aristas de la raíz), se deben haber listado todos los de nivel . Otros recorridos típicos del árbol son preorden, postorden e inorden:

  • El recorrido en preorden, también llamado orden previo consiste en recorrer en primer lugar la raíz y luego cada uno de los hijos en orden previo.
  • El recorrido en inorden, también llamado orden simétrico (aunque este nombre sólo cobra significado en los árboles binarios) consiste en recorrer en primer lugar , luego la raíz y luego cada uno de los hijos en orden simétrico.
  • El recorrido en postorden, también llamado orden posterior consiste en recorrer en primer lugar cada uno de los hijos en orden posterior y por último la raíz.

Finalmente, puede decirse que esta estructura es una representación del concepto de árbol en teoría de grafos. Un árbol es un grafo conexo y acíclico.

Tipos de árboles




Ejemplo de árbol (binario).

No hay comentarios:

Publicar un comentario