Bienvenido Aprende Programa Crea

ARBOLES EN C


^


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.





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.

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:
O generalizando más:
Declaraciones de tipos para manejar árboles en C

Para C, y basándonos en la declaración de nodo que hemos visto más arriba, trabajaremos con los siguientes tipos:
 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.

El movimiento a través de árboles, salvo que implementemos punteros al nodo padre, será siempre partiendo del nodo raíz hacia un nodo hoja. Cada vez que lleguemos a un nuevo nodo podremos optar por cualquiera de los nodos a los que apunta para avanzar al siguiente nodo.
En general, intentaremos que exista algún significado asociado a cada uno de los punteros dentro de cada nodo, los árboles que estamos viendo son abstractos, pero las aplicaciones no tienen por qué serlo. Un ejemplo de estructura en árbol es el sistema de directorios y ficheros de un sistema operativo. Aunque en este caso se trata de árboles con nodos de dos tipos, nodos directos y nodos fichero, podríamos considerar que los nodos hoja son ficheros y los nodos rama son directorios.
Otro ejemplo podría ser la tabla de contenido de un libro, por ejemplo de este mismo curso, dividido en capítulos, y cada uno de ellos en subcapítulos. Aunque el libro sea algo lineal, como una lista, en el que cada capítulo sigue al anterior, también es posible acceder a cualquier punto de él a través de la tabla de contenido.
También se suelen organizar en forma de árbol los organigramas de mando en empresas o en el ejército, y los árboles genealógicos.





struct nodo { int dato; 
struct nodo *rama1; 
struct nodo *rama2;
struct nodo *rama3; };

#define ORDEN 5 
 struct nodo { 
 int dato; 
 struct nodo *rama[ORDEN]; };





typedef struct _nodo { 
 int dato;
 struct _nodo *rama[ORDEN];
 } tipoNodo;
 typedef tipoNodo *pNodo;



MEMORIAS DINÁMICAS



Memoria dinámica aquella  que se reserva en tiempo de ejecución. Su principal ventaja frente a la estática, es que su tamaño puede variar durante la ejecución del programa.(En C, el programador es encargado de liberar esta memoria cuando no la utilice más). El uso de memoria dinámica es necesario cuando no conocemos el número de datos/elementos a tratar; sin embargo es algo más lento, ya que el tiempo ejecución depende del espacio que se va ha usar Hay que mencionar que la memoria estática es mas rápida ya que esta disponible desde que se inicio el programa.

Datos Dinámicos: 
Asignación de Memoria dinámica
►Los punteros proporcionan el soporte necesario para el potente sistema de asignación dinámica de memoria de C.
►La asignación dinámica es la forma en la que un programa puede obtener memoria mientras se está ejecutando.
►A las variables globales por ejemplo, se les asigna memoria en tiempo de compilación.
►Durante la ejecución no se pueden añadir variables globales o locales, pero existen ocasiones en las que un programa necesita usar cantidades de memoria variables.
TIPOS DE MEMORIAS:
NEW - Asigna memoria

New devuelve una referencia a una posición en memoria que  guarda el tipo indicado en la sentencia new;Devuelve un puntero. Si no hay suficiente memoria libre para satisfacer la petición, se da un fallo de asignación y devuelve un NULL.
El siguiente código asigna memoria para guardar datos de una estructura persona:

persona *p;
p = new persona;

DELETE – Libera memoria

Su tamaño y forma es variable (o puede serlo) a lo largo de un programa, por lo que se crean y destruyen en tiempo de ejecución. Esto permite dimensionar la estructura de datos de una forma precisa: se va asignando memoria en tiempo de ejecución según se va necesitando.



üLa instrucción delete es la opuesta a new porque devuelve al sistema la memoria previamente asignada.
üUna vez que la memoria ha sido liberada, puede ser reutilizada en una posterior llamada a new.
Ejemplo:
persona *p;
p = new persona;
  delete p;

La biblioteca estándar de C proporciona las funciones malloc, calloc, realloc free para el manejo de memoria dinámica. Estas funciones están definidas en el archivo de cabecera stdlib.h.

MALLOC
Reserva un bloque de memoria y devuelve un puntero vacio al inicio de la misma. Tiene la siguiente definición:
void *malloc(size_t size);

donde el parámetro size especifica el número de bytes a reservar. En caso de que no se pueda realizar la asignación, devuelve el valor nulo (definido en la macro NULL), lo que permite saber si hubo errores en la asignación de memoria.



CALLOC
Funciona de modo similar a malloc, pero además de reservar memoria, inicializa a 0 la memoria reservada. Se usa comúnmente para arreglos y matrices. Está definida de esta forma:
void *calloc(size_t nmemb, size_t size);
El parámetro nmemb indica el número de elementos a reservar, y size el tamaño de cada elemento. El ejemplo anterior se podría reescribir con calloc de esta forma:


REALLOC
La función realloc redimensiona el espacio asignado de forma dinámica anteriormente a un puntero. Tiene la siguiente definición:
void *realloc(void *ptr, size_t size);
Donde ptr es el puntero a redimensionar, y size el nuevo tamaño, en bytes, que tendrá. Si el puntero que se le pasa tiene el valor nulo, esta función actúa como malloc. Si la reasignación no se pudo hacer con éxito, devuelve un puntero nulo, dejando intacto el puntero que se pasa por parámetro. Al usar realloc, se debería usar un puntero temporal. De lo contrario, podríamos tener una fuga de memoria, si es que ocurriera un error en realloc.



FREE
La función free sirve para liberar memoria que se asignó dinámicamente. Si el puntero es nulo, free no hace nada. Tiene la siguiente definición:
void free(void *ptr);
El parámetro ptr es el puntero a la memoria que se desea liberar:
Una vez liberada la memoria, si se quiere volver a utilizar el puntero, primero se debe reservar nueva memoria con malloc o calloc:



EJEMPLO





LISTAS EN LENGUAJE C


una lista enlazada es una de las estructuras 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, enlaces o punteros al nodo anterior o posterior. El principal beneficio de las listas enlazadas respecto a los vectores 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 autorreferenciado porque contienen un puntero o enlace  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.

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 (o indica que tiene la dirección en memoria del siguiente nodo) en la lista, o al valor NULL o a la lista vacía, si es el último nodo.


Ejemplo  de una lista sencilla o lista simple:
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<alloc.h>

Aquí damos a conocer la Asignación de una lista como una estructura que almacena datos

struct nodo{
   int valor;        // Valor que va tener la estructura en cada uno en este caso es un numero entero
   struct nodo *siguiente; //Apuntador hacia el siguiente nodo o enlace
};

typedef struct nodo *TipoLista; //Definicion del nombre de la lista


//declaracion de funciones que posee la listas
void Insertar(TipoLista &l, int v  ); 
void EliminarRegistro(TipoLista &l, int v);
void VaciarLista(TipoLista &l);
int  ListaVacia(TipoLista l);
void MostrarLista(TipoLista l);
void Modificar(TipoLista l, int v,int n);

//*****************************

int main()
{
   TipoLista lista = NULL; Inicio de listas en Nulo
   int op,x,n;
   TipoLista p;
  
   do
   {
      clrscr(); //MENU DE OPCIONES
      printf("***MENU***\n\n");
      printf("1. Agregar dato\n");
      printf("2. Modificar dato\n");
      printf("3. Mostrar datos\n");
      printf("4. Eliminar dato\n");
      printf("5. Vaciar lista\n");
      printf("6. Salir\n\n");
      printf("Digite la opcion: ");
      scanf("%d",&op);
   
      switch(op)
      {
         case 1:
            do {  // SE CREAUNA LISTA HASTA QUE EL USUARIO PRECIONE 0
               clrscr();
               printf("Digite  Cero (0) para salir\n");
               printf("Digite el dato que desea agregar:");
               scanf("%d",&x);
               if (x!=0)
                  Insertar(lista,x);//ENVIO DE VALOR AL PROCESO DE INSERTAR DATOS A LISTA
            }while (x!=0);
         
            getch();
            break;
      
         case 2:
            MostrarLista(lista);   
            printf("\n Digite el dato que desea modificar:");
            scanf("%d",&x);            //MODIFICACION DE DATOS
            printf("\n Digite el dato nuevo:");
            scanf("%d",&n);    //LEE NUEVO DATO
            Modificar(lista,x,n); //ENVIO A FUNCION DE ENCONTRAR DATO Y CAMBIAR A ACTUAL
            getch();
            break;
      
         case 3:
            MostrarLista(lista);
            getch();
            break;
      
         case 4:
            MostrarLista(lista);
            printf("Digite el dato que desea eliminar:");
            scanf("%d",&x);
            EliminarRegistro(lista,x);  //ELIMINA DATO QUE  VERIFICA EL USUARIO
            getch();
            break;
      
         case 5:
          //  BorrarLista(lista);
            break;
      
         default:
            printf("Opcion no valida!\n");
            getch();
      }
   
   
   } while(op!=6);


   getchar();
   return 0;
}



//***********
//Lista Vacia
int ListaVacia(TipoLista lista)
{
   return (lista == NULL);
}
 //**************************
//Insertar registro
void Insertar(TipoLista &lista, int valor)
{
   TipoLista nuevo;
   nuevo = new(struct nodo);
   nuevo->valor = valor;
   nuevo->siguiente = lista;
   lista  = nuevo;
}
 //**************************
//Imprimir lista
void MostrarLista(TipoLista lista)
{
   TipoLista nodo = lista;

   if(ListaVacia(nodo)) printf("Lista vacia\n");
   else {
      while(nodo) {
         printf("%d -> ", nodo->valor);
         nodo = nodo->siguiente;
      }
      printf("\n");
   }
}
 //**************************
//Modificar
void Modificar(TipoLista lista, int v,int n)
{
   TipoLista nodo;
   int ban = 0;
   nodo = lista;

   while(nodo)
   {
      if(nodo->valor == v)
      {
         nodo->valor=n;
         ban=1;
         printf("Registro Modificado\n");
         MostrarLista(lista);
      }
      nodo = nodo->siguiente;
   }
   if(ban== 0)
      printf("No se encontro el registro\n");

}
 //**************************
//Eliminar
void EliminarRegistro(TipoLista &lista, int v)
{
   TipoLista nodo, anterior;
   nodo= lista;
   int ban =0;

   if(lista!=NULL)
   {
      while(nodo!=NULL)
      {
         if(nodo->valor==v)
         {
            ban=1;
            printf("Registro Eliminado\n");
            if(nodo==lista)
               lista = lista->siguiente;
            else
               anterior->siguiente = nodo->siguiente;
         
            delete(nodo);
            return;
         }
         anterior = nodo;
         nodo =nodo->siguiente;
      }
   }
   else
      printf(" Lista vacia..!\n");

   if(ban==0 )
      printf("No se encontro el registro\n");
}

//Vaciar toda la lista
void VaciarLista(TipoLista &lista)
{
   TipoLista nodo;
   // nodo= lista;

   if(lista!=NULL)
   {
      while(lista!=NULL)
      {
         nodo =lista;
         lista = lista->siguiente;
         delete(nodo);
      
      }
   }
   else
      printf(" Lista vacia..!\n");

}







COLAS EN C

Una cola es un tipo especial de lista abierta en la que sólo se pueden insertar nodos en uno de los extremos de la lista y sólo se pueden eliminar nodos en el otro. Además, como sucede con las pilas, las escrituras de datos siempre son inserciones de nodos, y las lecturas siempre eliminan el nodo leído.
Este tipo de lista es conocido como lista FIFO (First In First Out), el primero en entrar es el primero en salir.
El símil cotidiano es una cola para comprar, por ejemplo, las entradas del cine. Los nuevos compradores sólo pueden colocarse al final de la cola, y sólo el primero de la cola puede comprar la entrada.
El nodo típico para construir pilas es el mismo que vimos en los capítulos anteriores para la construcción de listas y pilas:
struct nodo {
   int dato;
   struct nodo *siguiente;
};

Declaraciones de tipos para manejar colas en C
Los tipos que definiremos normalmente para manejar colas serán casi los mismos que para manejar listas y pilas, tan sólo cambiaremos algunos nombres:

typedef struct _nodo {
   int dato;
   struct _nodo *siguiente;
} tipoNodo;
 typedef tipoNodo *pNodo;
typedef tipoNodo *Cola;

tipoNodo es el tipo para declarar nodos, evidentemente.
pNodo es el tipo para declarar punteros a un nodo.

Es evidente, a la vista del gráfico, que una cola es una lista abierta. Así que sigue siendo muy importante que nuestro programa nunca pierda el valor del puntero al primer elemento, igual que pasa con las listas abiertas. Además, debido al funcionamiento de las colas, también deberemos mantener un puntero para el último elemento de la cola, que será el punto donde insertemos nuevos nodos.
Teniendo en cuenta que las lecturas y escrituras en una cola se hacen siempre en extremos distintos, lo más fácil será insertar nodos por el final, a continuación del nodo que no tiene nodo siguiente, y leerlos desde el principio, hay que recordar que leer un nodo implica eliminarlo de la cola.

EJEMPLO
#include <stdlib.h>
#include <stdio.h>

typedef struct _nodo {
   int valor;
   struct _nodo *siguiente;
} tipoNodo;

typedef tipoNodo *pNodo;

/* Funciones con colas: */
void Anadir(pNodo *primero, pNodo *ultimo, int v);
int Leer(pNodo *primero, pNodo *ultimo);

int main()
{
   pNodo primero = NULL, ultimo = NULL;

   Anadir(&primero, &ultimo, 20);
   printf("Aniadir(20)\n");
   Anadir(&primero, &ultimo, 10);
   printf("Aniadir(10)\n");
   printf("Leer: %d\n", Leer(&primero, &ultimo));
   Anadir(&primero, &ultimo, 40);
   printf("Aniadir(40)\n");
   Anadir(&primero, &ultimo, 30);
   printf("Aniadir(30)\n");
   printf("Leer: %d\n", Leer(&primero, &ultimo));
   printf("Leer: %d\n", Leer(&primero, &ultimo));
   Anadir(&primero, &ultimo, 90);
   printf("Aniadir(90)\n");
   printf("Leer: %d\n", Leer(&primero, &ultimo));
   printf("Leer: %d\n", Leer(&primero, &ultimo));

   system("PAUSE");
   return 0;
}

void Anadir(pNodo *primero, pNodo *ultimo, int v)
{
   pNodo nuevo;

   /* Crear un nodo nuevo */
   nuevo = (pNodo)malloc(sizeof(tipoNodo));
   nuevo->valor = v;
   /* Este será el último nodo, no debe tener siguiente */
   nuevo->siguiente = NULL;
   /* Si la cola no estaba vacía, añadimos el nuevo a continuación de ultimo */
   if(*ultimo) (*ultimo)->siguiente = nuevo;
   /* Ahora, el último elemento de la cola es el nuevo nodo */
   *ultimo = nuevo;
   /* Si primero es NULL, la cola estaba vacía, ahora primero apuntará también al nuevo nodo */
   if(!*primero) *primero = nuevo;
}

int Leer(pNodo *primero, pNodo *ultimo)
{
   pNodo nodo; /* variable auxiliar para manipular nodo */
   int v;      /* variable auxiliar para retorno */
   
   /* Nodo apunta al primer elemento de la pila */
   nodo = *primero;
   if(!nodo) return 0; /* Si no hay nodos en la pila retornamos 0 */
   /* Asignamos a primero la dirección del segundo nodo */
   *primero = nodo->siguiente;
   /* Guardamos el valor de retorno */
   v = nodo->valor; 
   /* Borrar el nodo */
   free(nodo);
   /* Si la cola quedó vacía, ultimo debe ser NULL también*/
   if(!*primero) *ultimo = NULL;
   return v;
}

Copyright © 2013 Lenguaje c and Blogger Themes.