Apuntes para el curso de           “Estructuras de datos en C/C++ ”                    Dr. Abdiel E. C´ceres Gonz´lez     ...
3.   Colas                                             313.1. Estructura de las colas en C/C++                  323.2. Col...
1.   Preliminares de programaci´n en C/C++                               oEn esta secci´n recordaremos tres temas de progr...
float SalariosDeEmpleados[Max_empleados];...SalariosDeEmpleados = 45739.0;El efecto har´ cambiar la direcci´n inicial del ...
#define Coordenadas_Max 20#define Tamano_MaX_Compania_Id 15int CoordenadasDePantalla[Coordenadas_Max];char IDCompania[Tama...
#define maxDiasSemana 7int main(void){int desplazamiento, maxHorasDiarias[maxDiasSemana];cout<<"sizeof(int) es"<<(int)size...
for(despColumna=0;despColumna<numColumnas;despColumna++)std::cout<<" "<<despCalculados[despFila][despColumna]<<"   ";std::...
1.1.6.   Arreglos como argumentos de funcionesEs necesario recordar tres cosas al pasar arreglos como par´metros de fun-  ...
La salida del programa demuestra que el arreglo se pasa en llamada por refer-encia, ya que el primer ciclo for da como sal...
aeiouaeiouvalarray has exited with status 0.1.2. ApuntadoresDefinici´n 2 Un apuntador es una variable que contiene una dire...
Figura 3. Notaci´n de flecha para los apuntadores                               odireccionRAM = &contenidoRAM;*direccionRAM...
En C/C++ una variable apuntador contiene la direcci´n de un tipo de dato                                                  ...
En la l´ınea (04) se han declarado tres variables de tipo entero, se da a cadacelda un nombre y se inicializan 2 de ´stas....
Este puede ser un error muy dif´ de localizar puesto que muchos compi-                                ıcilladores no emite...
1.2.3.   Utilizaci´n incorrecta del operador de direcci´n                  o                                    oNo se pue...
tipo_miembro miembro_3;  :  :  tipo_miembro miembro_n;};Un punto y coma finaliza la definici´n de una estructura puesto que ...
char sztitular[iString20+iNull_char];  int ianio;  long int lhoras_motor;  float fprecioventa;} stbarco_usado;1.3.2.   Uti...
struct Fraction {// declaramos sus dos miembrosint numerator;int denominator;}; // Note el punto y coma al final// funcion...
<< f.denominator << "n";}Nota sobre las funciones prototipos:Las funciones prototipo tienen los siguientes usos importante...
while i>0 and A[i]>key         do A[i+1]=A[i]             i:=i-1      A[i+1]:=key   a) desarrolle un programa en C/C++ del...
2.   La pilaUno de los conceptos m´s utiles en las ciencias de la computaci´n es el de pila.                       a ´    ...
Figura 10. Operaci´n de insertar el elemento G en la pila P                             oCuando se desea retirar un elemen...
Cuando se termina de ejecutar alg´n procedimiento, se recupera el registro que                                    uest´ en...
2.2. Operaciones b´sicas                  aLas operaciones b´sicas de una pila son:                 a1.   En la pila S, in...
un false si la pila tiene al menos un elemento, es decir:                                                                ...
‘*’ :   nada que hacer‘4’ :   nada que hacer‘)’ :   v=pop(S)‘/’ :   nada que hacer‘(’ :   push(S,‘(’)‘17’:   nada que hace...
fin de la nueva estructuraF´cilmente podemos describir un c´digo en C/C++ que represente lo anterior- a                   ...
2.5.2.   La operaci´n pop                   oLa operaci´n pop se escribe en forma de c´digo en C/C++ con la siguiente     ...
2.5.4.   La operaci´n stacktop                   oEste es un caso especial porque no se requiere hacer ning´n c´digo.     ...
operador y tomar los operandos necesarios siguientes (dos si se trata       de un operador binario y uno si es un operador...
3.   ColasDefinici´n 5 Las colas son una estructura de datos similar a las pilas. Recorde-         omos que las pilas funci...
Te´ricamente no hay l´   o                    ımite para el tama˜o de la cola, asi que siempre se                         ...
}y finalmente la operaci´n empty(Q):                      obool empty(struct cola *C){if(C->front>C->rear)return true;elser...
del arreglo.Cuando se requiere eliminar un dato de una cola de prioridad se necesitaverificar cada uno de los elementos alm...
carros. Los autos llegan por el extremo sur del estacionamiento y salen porel extremo norte del mismo. Si llega un cliente...
4.    Recursi´n             oUn tema fundamental para los pr´ximos temas es el de recusri´n. La recursi´n                 ...
!n = n ∗ !(n − 1)El programa en C/C++ es el que se muestra a continuaci´n:                                                ...
4.0.1.   La serie FibonacciUna de las series m´s famosas es sin duda alguna la serie de Fibonacci:                   a    ...
                      1                                                                       Si n = 1 ´ n = 2         ...
1. El paso base: Esta es la clave para terminar la recursi´n, es cuando deja                                              ...
b´squeda termina.      u     Si el arreglo tiene m´s de 1 elemento, tendremos que dividir en dos el                       ...
5.     ListasHay dos desventajas serias con respecto a las estructuras est´ticas de pilas y                               ...
Figura 17. Relaci´n “toma el curso de” para los conjuntos A de personas y B de                 omaterias.         R = {(di...
Notemos que el conjunto A de aristas puede ser un conjunto vac´ pero de                                                   ...
Como cada elemento puede tener a lo m´s una arista dirigida que sale y una                                          aarist...
Enseguida vamos a dar una lista de t´rminos usados para manejar los elemen-                                     etos de un...
info(p)=6;Despu´s de esstablecer la parte de informaci´n es necesario establecer la parte       e                         ...
next(p)=list;list=p;Eliminar un elemento de la lista. Para eliminar un elemento del iniciode la lista, se siguen los mismo...
( 4) int next;( 5) };( 6) struct nodeType node[numNodes];int main (int argc, char * const argv[]) {     std::cout << "Hell...
int getNode(void){    int p;    if (avail==-1){        std::cout<<"Overflown";        exit(1);    }    p=avail;    avail=n...
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Eda1
Upcoming SlideShare
Loading in …5
×

Eda1

828 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
828
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
9
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Eda1

  1. 1. Apuntes para el curso de “Estructuras de datos en C/C++ ” Dr. Abdiel E. C´ceres Gonz´lez a a ITESM-CCM 2 de junio de 2005ResumenUna estructura de datos es una manera de almacenar y organizar datos para facilitarel acceso y modificaciones. No hay una estructura de datos que sirva para todos losprop´sitos, y por eso es importante saber sus ventajas y desventajas. Este documen- oto es una colecci´n de apuntes para el curso de Estructuras de Datos. Los apuntes ose han tomado de algunas fuentes que son detalladas en la secci´n de bibliograf´ o ıa.´Indice 1. Preliminares de programaci´n en C/C++ o 3 1.1. Arreglos 3 1.2. Apuntadores 10 1.3. Estructuras C/C++ 15 1.4. Ejercicios de programaci´n o 19 2. La pila 21 2.1. Definici´n y ejemplos o 21 2.2. Operaciones b´sicas a 24 2.3. Ejemplo: N´mero de par´ntesis u e 25 2.4. La estructura de datos Pila en C/C++ 26 2.5. La representaci´n en C/C++ de las operaciones de una pila o 27 2.6. Problemas de programaci´n o 29 1
  2. 2. 3. Colas 313.1. Estructura de las colas en C/C++ 323.2. Colas con prioridad 333.3. Ejercicio de programaci´n o 344. Recursi´n o 364.1. Peligros en la recursividad 394.2. Ejercicios de programaci´n o 405. Listas 425.1. Grafos 425.2. Listas simplemente encadenadas 445.3. El uso de memoria din´mica en C/C++ a 515.4. Listas ligadas usando memoria din´mica a 545.5. Ejercicios de programaci´n o 566. ´ Arboles 576.1. Concepto general de ´rbol a 57 ´6.2. Arboles binarios 576.3. Representaci´n en C/C++ de los ´rboles binarios o a 64 ´6.4. Arboles 666.5. Ejercicios de programaci´n o 697. Grafos 717.1. Recordatorio de las definiciones 717.2. Aplicaci´n ejemplo o 73 2
  3. 3. 1. Preliminares de programaci´n en C/C++ oEn esta secci´n recordaremos tres temas de programaci´n en C/C++ que son o ofundamentales para estudiar estructuras de datos; estos temas son los arreg-los, los registros y los punteros. Los tres temas han sido tomados fundamen-talmente de [MP97]1.1. ArreglosDefinici´n 1 Un arreglo se compone de elementos de igual tama˜o almace- o nnados linealmente en posiciones de memoria consecutiva.Se puede acceder a cada elemento de datos individual utilizando un sub´ ındice,o´ındice, para seleccionar uno de los elementos. En C/C++ , un arreglo no esun tipo de datos est´ndar; es un tipo agregado compuesto de cualquier otro atipo de datos.Los arreglos se pueden definir usando tipos de datos mixtos debido a que sesupone que todos los elementos son del mismo tama˜o. Puesto que todos los nelementos son del mismo tama˜o y ya que este hecho se utiliza para ayudar na determinar c´mo localizar un elemento dado, resulta que los elementos son oalmacenados en localidades de memoria contiguas.Lo m´s importante a tener en cuenta es: El nombre de un arreglo es visto por el acompilador como un puntero-constante al primer elemento del arreglo. Esto esmuy importante: a) El nombre del arreglo es visto como un tipo puntero, y m´s aespec´ ıficamente, b) un puntero constante -significa una direcci´n de memoria obloqueada para el primer elemento de un arreglo-. Por ejemplo, aunque unadeclaraci´n de arreglo toma la f´rma gen´rica: o o eTipo_ElementoArray NombreArray [ NumeroDeElementos ]El compilador ve la declaraci´n como oTipo_ElementoArray * const NombreArray = &NombreArray[0];Por esta raz´n, un identificador de arreglo no puede ser usado nunca como un ovalor-i (valor izquierdo). Los valores izquierdos representan variables que sucontenido puede ser alterado por el programa; frecuentemente aparecen a laizquierda de las sentencias de asignaci´n. oSi los nombres de arreglo fueran variables izquierdos permitidos, el programapodr´ cambiar sus contenidos. ıa 3
  4. 4. float SalariosDeEmpleados[Max_empleados];...SalariosDeEmpleados = 45739.0;El efecto har´ cambiar la direcci´n inicial del propio arreglo. ıa o1.1.1. Declaraciones de un arregloLa sintaxis de declaraci´n de arreglos es: otipo nombre_arreglo [numero_de_elementos];Los siguientes son dos ejemplos de declaraciones de arreglos v´lidas en C/C++ a:int CoordenadasDePantalla[5]; /*Un arreglo de 5 enteros */char IDCompania[20]; /*Un arreglo de 20 caracteres */Figura 1. Arreglo CoordenadasDePantalla con ´ ındices de desplazamiento v´lido aEn la figura 1 se muestra el primer arreglo que fue declarado con el tipode n´meros enteros, llamado CoordenadasDePantalla, ocupa en memoria 5 ulocalidades de memoria contiguas, cada una de ellas capaz de almacenar unn´mero entero. Actualmente es com´n que los n´meros enteros sean de 32 u u ubits, esto hace que el arreglo CoordenadasDePantalla ocupe 32 × 5 = 160bitsNo se permite utilizar nombres de variables dentro de los corchetes. Por esto noes posible evitar la especificaci´n del tama˜o del arreglo hasta la ejecuci´n del o n oprograma. La expresi´n debe ser un valor constante, para que el compilador osepa exactamente cu´nto espacio de memoria tiene que reservar para el arreglo. aUna buena pr´ctica de programaci´n es usar constantes predefinidas. a o 4
  5. 5. #define Coordenadas_Max 20#define Tamano_MaX_Compania_Id 15int CoordenadasDePantalla[Coordenadas_Max];char IDCompania[Tamano_MaX_Compania_Id];El uso de constantes predefinidas garantiza que futuras referencias al arreglono excedan el tama˜o del arreglo definido. n1.1.2. Iniciaci´n del arreglo oC/C++ proporciona 3 maneras de iniciar elementos del arreglo:Por defecto: Cuando son creados, se aplica solamente a arreglos globales y est´ticos. aExpl´ıcita: Cuando son creados, suministrando datos de iniciaci´n oTiempo de ejecuci´n: Durante la ejecuci´n del programa cuando se asig- o o nan o copias datos en el arreglo.1.1.3. Acceso a los elementos de un arregloSi se tiene un error cuando se utilizan arreglos en C/C++ , de seguro el errorinvolucra el acceso a los elementos del arreglo, por la simple raz´n de que oel primer elemento est´ en una posici´n 0, no 1. De manera que el ultimo a o ´elemento del arreglo lo encontramos en n-1, donde n es el n´mero de elementos. uSupongamos la siguiente declaraci´n: oint Estado[Rango_Maximo_Estado]={-1,0,1};La siguiente sentencia tiene acceso a -1:Estado[0];Si escribimos Estado[3] causar´ un error porque no hay 4 elementos. a1.1.4. C´lculo del tama˜o de un arreglo (sizeof()) a nEs frecuente utilizar el operador sizeof() para calcular la cantidad de espacioque se necesita almacenar para un objeto:/* * exploresz.cpp */#include<iostream.h> 5
  6. 6. #define maxDiasSemana 7int main(void){int desplazamiento, maxHorasDiarias[maxDiasSemana];cout<<"sizeof(int) es"<<(int)sizeof(int)<<"nn";for(desplazamiento=0;desplazamiento<maxDiasSemana;desplazamiento++)cout<<"&maxHorasDiarias["<<desplazamiento<<"]="<<&maxHorasDiarias[desplazamiento]<<"n";return 0;}1.1.5. Arreglos multidimensionalesEl t´rmino dimensi´n representa el n´mero de ´ e o u ındices utilizados para referirsea un elemento particular en el arreglo. Los arreglos de m´s de una dimensi´n a ose llaman arreglos multidimensionales./*/ dosDim.cpp*/#include <iostream>#define numFilas 4#define numColumnas 5int main (int argc, char * const argv[]) { int despFila, despColumna, desplazamiento, multiplo,despCalculados[numFilas][numColumnas];for(despFila=0;despFila<numFilas;despFila++)for(despColumna=0;despColumna<numColumnas;despColumna++){desplazamiento=numColumnas-despColumna;multiplo=despFila;despCalculados[despFila][despColumna]=(despFila+1)*despColumna+desplazamiento * multiplo;};for(despFila=0;despFila<numFilas;despFila++){std::cout<<"Fila actual: "<<despFila<<"n";std::cout<<"Distancia relativa desde la base: "<<"n"; 6
  7. 7. for(despColumna=0;despColumna<numColumnas;despColumna++)std::cout<<" "<<despCalculados[despFila][despColumna]<<" ";std::cout<<"nn";} return 0;}}El programa utiliza dos ciclos for para calcular e inicial cada uno de loselementos del arraglo a su respectiva distancia relativa desde la base. El arreglocreado tiene 4 filas y 5 columnas por fila, haciendo un total de 20 elementosenteros.Los arreglos multidimensionales son almacenados de forma lineal en la memo-ria de la computadora. Los elementos en los arreglos multidimensionales est´n aagrupados desde el ´ındice m´s a la derecha hacia el centro. En el ejemplo an- aterior, fila 1, columna 1 ser´ el elemento 3 del arreglo almacenado. Aunque el ıac´lculo del desplazamiento aparece un poco dif´ es referenciado f´cilmente a ıcil, acada elemento del arreglo.La salida del programa anterior es:Fila actual: 0Distancia relativa desde la base: 0 1 2 3 4Fila actual: 1Distancia relativa desde la base: 5 6 7 8 9Fila actual: 2Distancia relativa desde la base: 10 11 12 13 14Fila actual: 3Distancia relativa desde la base: 15 16 17 18 19dosdim has exited with status 0. 7
  8. 8. 1.1.6. Arreglos como argumentos de funcionesEs necesario recordar tres cosas al pasar arreglos como par´metros de fun- aciones:1. Todos los arreglos son pasados en llamada-por referencia.2. Debido a que el arreglo es pasado en llamada por referencia, ser´ in-ıa correcto para la funci´n llamada devolver el arreglo en una sentencia o return();. Esta sentencia est´ de m´s. a a3. Todos los elementos del arreglo son pasados a las funciones en llamada por valor. lo que significa que se pasa una copia del elemento, no la direcci´n o del elemento./*// ereArray.xcode*/#include <iostream>#include <ctype.h>#define maxArray 5void ArrayMayuscula(char Array[maxArray]);int main (int argc, char * const argv[]) {int desplazamiento;char Array[maxArray]={’a’,’e’,’i’,’o’,’u’};for(desplazamiento=0;desplazamiento<maxArray;desplazamiento++)std::cout<<Array[desplazamiento];std::cout<<"n";ArrayMayuscula(Array);for(desplazamiento=0;desplazamiento<maxArray;desplazamiento++)std::cout<<Array[desplazamiento];return 0;}void ArrayMayuscula(char Array[maxArray]){for(int desplazamiento=0;desplazamiento<maxArray;desplazamiento++)Array[desplazamiento]=toupper(Array[desplazamiento]);//Aqui return array seria incorrecto} 8
  9. 9. La salida del programa demuestra que el arreglo se pasa en llamada por refer-encia, ya que el primer ciclo for da como salida los contenidos de min´sculas uoriginales: aeiou, mientras que el segundo ciclo for en main() da como salidalos contenidos del arreglo despu´s del llamado a la funci´n ArrayMayuscula(): e oAEIOU.Claramente, dentro del cuerpo de la funci´n ArrayMayuscula(), ha cambiado oel arreglo de regreso en la funci´n main(). el siguiente ejemplo es una simple omodificaci´n de este algoritmo, s´lo que en vez de pasar el arreglo completo, o ose pasa cada elemento individual:/*// ereArray2.xcode*/#include <iostream>#include <ctype.h>#define maxArray 5void ElementosArrayMayuscula(char unChar);int main (int argc, char * const argv[]) {int desplazamiento;char Array[maxArray]={’a’,’e’,’i’,’o’,’u’};for(desplazamiento=0;desplazamiento<maxArray;desplazamiento++)std::cout<<Array[desplazamiento];std::cout<<"n";for(desplazamiento=0;desplazamiento<maxArray;desplazamiento++)ElementosArrayMayuscula(Array[desplazamiento]);for(desplazamiento=0;desplazamiento<maxArray;desplazamiento++)std::cout<<Array[desplazamiento];return 0;}void ElementosArrayMayuscula(char unChar){unChar=toupper(unChar);}La salida del programa es: 9
  10. 10. aeiouaeiouvalarray has exited with status 0.1.2. ApuntadoresDefinici´n 2 Un apuntador es una variable que contiene una direcci´n de o omemoria.Supongamos una variable de tipo entero que se llama contenidoRAM y otravariable que se llama direccionRAM que puede contener una variable de tipoentero. En C/C++ una variable precedida del operador & devuelve la direcci´n ode la variable en lugar de su contenido. As´ que para asignar la direcci´n de ı ouna variable a otra variable del tipo que contiene direcciones se usan sentenciascomo esta:direccionRam = &contenidoRAM Figura 2. contenidoRAM se asigna a la localidad de memoria con direcci´n 7751 oEn la figura 2 se ilustra el nombre de la variable contenidoRAM y se observaque se encuentra en la direcci´n 7751 de la memoria. El contenido de esta olocalidad no se muestra. Una variable que contiene una direcci´n, tal como odireccionRAM, se llama variable apuntador o simplemente apuntador.Despues que la sentencia anterior se ejecuta, la direcci´n de contenidoRAM oser´ asignada a la variable apuntador direccionRAM. La relaci´n se expresa a odiciendo que direccionRAM apunta a contenidoRAM. La figura 3 ilustra estarelaci´n. oEl accceso al contenido de una celda cuya direcci´n est´ almacenada en la o avariable direccionRAM es tan sencillo como poner al inicio de la variableapuntador un asterisco: *direccionRAM. Lo que se ha hecho es eliminar lareferencia directa. Por ejemplo, si se ejecutan las siguientes dos sentencias, elvalor de la celda llamada contenidoRAM ser´ de 20 (v´ase la figura 4). a e 10
  11. 11. Figura 3. Notaci´n de flecha para los apuntadores odireccionRAM = &contenidoRAM;*direccionRAM = 20; Figura 4. A contenidoRAM se le asigna el valor entero 201.2.1. Declaraciones de variables apuntadorC/C++ requiere una definici´n para cada variable. Para definir una variable oapuntador direccionRAM que pueda contener la direcci´n de una variable oint, se escribe:int *direccionRAM;Realmente existen dos partes separadas en esta declaraci´n. El tipo de dato ode direccionRAM es:int *y el identificador para la variable esdireccionRAMEl asterisco que sigue a int significa “apuntador a”. Esto es, el siguiente tipode dato es una variable apuntador que puede contener una direcci´n a un int: oint * 11
  12. 12. En C/C++ una variable apuntador contiene la direcci´n de un tipo de dato oparticular:char *direccion_char;char *direccion_int;El tipo de dato de direccion char es diferente del tipo de dato de la variableapuntador direccion int. En un programa que define un apuntador a untipo de dato y utliza ´ste para apuntar a otro tipo de dato, pueden ocurrir eerrores en tiempo de ejecuci´n y advertencias en tiempo de compilaci´n. Una o opr´ctica de programaci´n pobre ser´ definir un apuntador de una forma y a o ıaluego utilizar ´ste de alguna otra forma. Por ejemplo: eint *direccion_int;float un_float = 98.34;direccion_int = &un_float;1.2.2. Utilizaci´n de punteros en sentencias sencillas oVeamos el siguiente ejemplo:/*// changeVals.xcode*/(01) #include <iostream>(02)(03) int main (int argc, char * const argv[]) {(04) int A_int=15, B_int=37, Temp_int;(05) int *direccion_int;(06)(07) std::cout<<"El contenido de A_int es:"<<A_int<<"n";(08) std::cout<<"El contenido de B_int es:"<<B_int<<"n";(09) direccion_int = &A_int;(10) Temp_int = *direccion_int;(11) *direccion_int = B_int;(12) B_int = Temp_int;(13) std::cout<<"Despues del intercambio:"<<"nn";(14)(15) std::cout<<"El contenido de A_int es:"<<A_int<<"n";(16) std::cout<<"El contenido de B_int es:"<<B_int<<"n";(17) return 0;(18) } 12
  13. 13. En la l´ınea (04) se han declarado tres variables de tipo entero, se da a cadacelda un nombre y se inicializan 2 de ´stas. Supondremos que la direcci´n de e omemoria asignada para la variable A int es la direcci´n 5328, y la direcci´n o oen memoria RAM asignada para la variable B int es la direcci´n 7916, y la ocelda llamada Temp int se le ha asignado la direcci´n 2385. V´ase la figura 5; o e Figura 5. Descripci´n de las tres variables en la memoria oEn la l´ ınea (05) se define un apuntador a un tipo de dato entero llamadodireccion int. La sentencia asigna la celda y da a ´sta un nombre. eLuego, en la l´ ınea (09), la tercera sentencia asigna a direccion_int la direc-ci´n de A_int (figura 6). o Figura 6. direccion int dada la direcci´n de A int oLa l´ ınea (10) utiliza la expresi´n *direccion_int para acceder al contenido ode la celda a la cual apunta direccion_int:Temp_int = *direccion_int;Por consiguiente, el valor entero 15 se almacena en la variable Temp_int. Sino se pone el * enfrente de direccion_int;, la sentencia de asignaci´n al- omacenar´ ilegalmente el contenido de direccion_int en la celda nombrada ıaTemp_int, pero se supone que Temp_int contiene un entero, no una direcci´n. o 13
  14. 14. Este puede ser un error muy dif´ de localizar puesto que muchos compi- ıcilladores no emiten ninguna advertencia/error.Para empeorar el asunto, la mayor´ de los apuntadores son cercanos, lo que ıasignifica que ocupan 2 bytes (4 bytes para aplicaciones de 32-bits), el mismotama˜o que un entero en una PC. nLa sentencia (11) copia el contenido de la variable B int en la celda apuntadapor la direcci´n almacenada en direccion int(figura 7): o*direccion_int = B_int;Figura 7. Se copia el contenido de B int usando la notaci´n de flecha de apuntadores oLa ultima sentencia en la l´ ´ ınea (12) simplemente copia el contenido de unavariable entera, Temp int en otra variable entera B int (figura 8 Figura 8. Se copia Temp int en B int utilizando asignaci´n normal. oDebemos de asegurarnos de comprender la diferencia entre qu´ se referencia ecuando una variable puntero est´ precedida por el operador de indirecci´n y a ocu´ndo no est´ precedida por este operador. a aPara este ejemplo, la primera sintaxis es un apuntador a una celda que puedecontener un valor entero. La segunda sintaxis referencia la celda que contienela direcci´n de otra celda que puede contener un entero. o 14
  15. 15. 1.2.3. Utilizaci´n incorrecta del operador de direcci´n o oNo se puede utilizar el operador de direcci´n sobre toda expresi´n C/C++ . El o osiguiente ejemplo demuestra aquellas situaciones donde no se puede aplicar eloperador de direcci´n &. opuedeAlmacenarDireccionDeConstante = &37;int RAM_int = 5;puedeAlmacenarDireccionDeExpresionTemp = &(RAM_int +15);puedeAlmacenarDireccionDeRegistro = &varRegistro;La primera sentencia trata de obtener ilegalmente la direcci´n de un valor oconstante integrado. La sentencia no tiene sentido puesto que 37 no tiene unacelda de memoria asociada con ´ste. eLa segunda sentencia de asignaci´n intenta devolver la direcci´n de la expre- o osi´n RAM_int+15. No existe direcci´n asociada con la expresi´n puesto que la o o oexpresi´n en s´ misma es realmente un proceso de manipulaci´n de pila. o ı oNormalmente, el ultimo ejemplo respeta la demanda del programador para ´definir varRegistro como un registro m´s que como una celda de almace- anamiento en la memoria interna. Por consiguiente, no podr´ devolverse y ıaalmacenarse la direcci´n de celda de memoria. El compilador C/C++ da la omemoria de variable, no el almacenamiento de registro.1.3. Estructuras C/C++Definici´n 3 Una estructura es un grupo de variables las cuales pueden ser ode diferentes tipos sostenidas o mantenidas juntas en una sola unidad. Launidad es la estructura.1.3.1. Sintaxis y reglas para estructuras en C/C++En C/C++ se forma una estructura utilizando la palabra reservada struct,seguida por un campo etiqueta opcional, y luego una lista de miembros dentrode la estructura. La etiqueta opcional se utiliza para crear otras variables deltipo particular de la estructura:struct campo_etiqueta{ tipo_miembro miembro_1; tipo_miembro miembro_2; 15
  16. 16. tipo_miembro miembro_3; : : tipo_miembro miembro_n;};Un punto y coma finaliza la definici´n de una estructura puesto que ´sta es o erealmente una sentencia C/C++ . Algunos de los ejemplos usan la estructura:struct stbarco{ char sztipo[iString15+iNull_char]; char szmodelo[iString15+iNull_char]; char sztitular[iString20+iNull_char]; int ianio; long int lhoras_motor; float fprecioventa;};En un programa, podemos asociar una variable con una estructura utilizandouna sentencia similar a la siguiente:struct stbarco stbarco_usado;La sentencia define stbarco_usado de tipo struct stbarco. La declaraci´n orequiere el uso del campo etiqueta de la estructura. Si esta sentencia est´ con- atenida dentro de una funci´n, entonces la estructura, llamada stbarco_usado, otiene un ´mbito local a esa funci´n. Si la sentencia est´ contenida fuera de a o atodas las funciones de programa, la estructura tendr´ un ´mbito global. Es a aposible declarar una variable usando esta sintaxis:struct stbarco{ char sztipo[iString15+iNull_char]; char szmodelo[iString15+iNull_char]; char sztitular[iString20+iNull_char]; int ianio; long int lhoras_motor; float fprecioventa;} stbarco_usado;Aqu´ la declaraci´n de variable va antes del punto y coma final. Cuando se ı oasocia s´lo una variable con el tipo estructura, el campo etiqueta puede ser oeliminado, por lo que ser´ posible escribir: ıastruct { char sztipo[iString15+iNull_char]; char szmodelo[iString15+iNull_char]; 16
  17. 17. char sztitular[iString20+iNull_char]; int ianio; long int lhoras_motor; float fprecioventa;} stbarco_usado;1.3.2. Utilizaci´n de miembros de estructuras oPara accesar a los miembros de las estructuras se usa el punto u operadormiembro (.). La sintaxis es:estructuraNombre.miembroNombrePor ejemplo en:gets(stbarco_usado.szmodelo);Aqu´ stbarco_usado es el nombre asociado con la estructura, y szmodelo es ı,una variable miembro de la estructura, otro ejemplo:std::cin>> stbarco_usado.sztipo;Esta sentencia leer´ la marca del stbarco_usado en el arreglo de caracteres, amientras la pr´xima sentencia imprimir´ el precio de venta de stbarco_usado o aen la pantalla.srd::cout<< stbarco_usado.fprecioventa;Ejemplo de estructuras:/* fractionStruct.cpp -Programa para demostrar el uso de lostipos Struct en C++, este tipo de datoses util para los programadores para crearsus propias estructuras de tipos.*/#include <iostream>using namespace std;// Definimos un nuevo tipo de estructura llamada Fraction// como la definicion se puso antes del "main"// los tipos Fraction se pueden usar como prototipos 17
  18. 18. struct Fraction {// declaramos sus dos miembrosint numerator;int denominator;}; // Note el punto y coma al final// funciones prototiposvoid getFraction(Fraction &f);void printFraction(const Fraction &f);int main (int argc, char * const argv[]){// declaramos variables de tipo FractionFraction f1, f2;// obtenemos dos fracciones y las desplegamosgetFraction(f1);cout << "nf1 = ";printFraction(f1);getFraction(f2);cout << "nf2 = ";printFraction(f2);cout << endl;return 0;}// pedimos al usuario los valores del denominador y numerador// los almacenamos en su adecuado lugar en la estrcututra; checamos si// el valor del denominador es valido y lo ponemos en 1 si no lo es.void getFraction(Fraction &f) {cout << "nEnter the numerator: ";cin >> f.numerator;cout << "Enter the denominator: ";cin >> f.denominator;if (f.denominator == 0) {cout << "nIllegal denominator! Denominator is being set to 1.n";f.denominator = 1;}}// imprimimos la fraccionvoid printFraction(const Fraction &f) {cout << f.numerator << "/" 18
  19. 19. << f.denominator << "n";}Nota sobre las funciones prototipos:Las funciones prototipo tienen los siguientes usos importantes: Establecen el tipo devuelto para las funciones que devuelven otros tipos diferentes que int. Aunque las funciones que devuelven valores enteris no necesitan prototipos, se recomienda tener prototipos. Sin prototipos completos, se hacen las conversiones est´ndares, pero no se a checan los tipos o los n´meros de argumentos con el n´mero de par´metros. u u a Los prototipos se usan para inicializar apuntadores a funciones, antes de que las funciones sean definidas. La lista de par´metros se usa para checar la correspondencia de los argu- a mentos en la llamada a la funci´n con los par´metros en la definici´n de la o a o funci´n oconst en parmetros de funcionesEl especificador const puede ser utilizado en la definici´n de par´metros de o afunciones. Esto resulta de especial utilidad en tres casos. En los tres el finque se persigue es el mismo: indicar que la funci´n no podr´ cambiar dichos o aargumentos: Con par´metros de funciones que sean de tipo matriz (que se pasan por a referencia). Ejemplo: int strlen(const char[]); Cuando los par´metros son punteros (a fin de que desde dentro de la funci´n a o no puedan ser modificados los objetos referenciados). Ejemplo: int printf (const char *format, ...); Cuando el argumento de la funci´n sea una referencia, previniendo as´ que la o ı funci´n pueda modificar el valor referenciado. Ejemplo: int dimen(const o X &x2);1.4. Ejercicios de programaci´n o1. El siguiente algoritmo es el m´todo de inserci´n para ordenar elementos e o en un arreglo: insertionSort(A) for j:=2 to length[A] do key:=A[j] -> Inserta el elemento A[j] -> en la secuencia ordenada A[1..j-1] i:=j-1 19
  20. 20. while i>0 and A[i]>key do A[i+1]=A[i] i:=i-1 A[i+1]:=key a) desarrolle un programa en C/C++ del m´todo de inserci´n e o b) ilustre c´mo opera el algoritmo insertionSort(A) usando como en- o trada el arreglo A=<31,41,59,26,41,58>2. Reescriba el programa y n´mbrelo insertionSortNondec para que or- o dene los elementos en orden decreciente3. Considere el siguiente problema de b´squeda: u Input: Una secuencia de n n´meros A = a1 , a2 , . . . , an y un valor v. u Output: Un ´ ındice i tal que v = A[i] o el valor espacial N IL si v no ocurre en A. Escriba un programa que resuelva este problema de b´squeda. u4. Considere el problema de sumar dos n´meros binarios de longitud n. u Cada n´mero se almacena en uno de los arreglos A y B de tama˜o n. La u n suma se almacena en un arreglo C de tama˜o n + 1, tambi´n como un n e n´mero binario. Escriba un programa que resuelva este problema. u 20
  21. 21. 2. La pilaUno de los conceptos m´s utiles en las ciencias de la computaci´n es el de pila. a ´ oEn esta secci´n vamos a definir este concepto de manera abstracta y veremos oc´mo se usa para convertirse en una herramienta concreta y de gran valor en olas soluciones de problemas. La informaci´n contenida en esta secci´n se ha o otomado de [TA83].2.1. Definici´n y ejemplos oDefinici´n 4 Una pila (stack) es una colecci´n ordenada de elementos en la o ocual se pueden insertar nuevos elementos por un extremo y se pueden retirarotros por el mismo extremo; ese estremos se llama “la parte superior” de lapila.Si tenemos un par de elementos en la pila, uno de ellos debe estar en la partesuperior de la pila, que se considera “el m´s alto” en la pila que el otro. En ala figura 9 el elemento F es el m´s alto de todos los elementos que est´n en la a apila. El elemento D es el m´s alto de los elementos A,B,C, pero es menor que alos elementos E y F. Figura 9. Pila con 6 elementosPara describir c´mo funciona esta estructura, debemos agregar un nuevo ele- omento, el elemento G. Despu´s de haber agregado el elemento G a la pila, la enueva configuraci´n es la que se muestra en la figura 10. oDe acuerdo con la definici´n, existe solamente un lugar en donde cualquier oelemento puede ser agregado a la pila. Despu´s de haber insertado el nuevo eelemento, G ahora es el elemento en la cima. Debedos aclarar en qu´ pila edeseamos insertar elementos, puesto que es posible tener m´s de una pila al amismo tiempo. 21
  22. 22. Figura 10. Operaci´n de insertar el elemento G en la pila P oCuando se desea retirar un elemento de la pila, solo basta ordenar que searetirado un elemento; no podemos decir “retira C de la pila”, porque C noest´ en la cima de la pila y solamente podemos retirar el elemento que est´ en a ala cima. Para que la sentencia “retira C de la pila” tenga sentido, debemosreplantear las ´rdenes a algo como: oRetira de la pila hasta que el elemento retirado sea C.Ni siquiera es necesario decir: “Retira un elemento de la pila...” porque essobreentendido que solamente se puede sacar un elemento a la vez.Siguiendo nuestro ejemplo, ahora deseamos retirar de la pila P. La configu-raci´n global de la pila es como se muestra en la figura 11 o Figura 11. Operaci´n de retirar de la pila P oEl concepto de pila es muy importante en computaci´n y en especial en teor´ o ıade lenguajes de programaci´n. En lenguajes procedurales como Pascal o C, la opila es una estructura indispensable, debido a las llamadas a funci´n. oResulta que el flujo de instrucciones va de arriba hacia abajo, y cuando ocurreuna llamada a alguna funci´n, el estado global del sistema se almacena en un oregistro y ´ste en una pila. As´ que la pila va a contenr todas las llamadas a e ıprocedimientos que se hagan. 22
  23. 23. Cuando se termina de ejecutar alg´n procedimiento, se recupera el registro que uest´ en la cima de la pila. En ese registro est´n los valores de las variables como a aestaban antes de la llamada a la funci´n, o algunas pueden haber cambiado si ovalor, dependiendo del ´mbito de las variables. aCada elemento en la pila que es retirado, significa que se ha terminado deejecutar alguna funci´n. Cuando se termina de ejecutar el programa, la pila ode llamadas a subprogramas debe haber quedado en 0 tambi´n, de otro modo epodr´ causar algun tipo de error. ıaEsto nos lleva a pensar en otras utilidades de la pila. La pila sirve para en-contrar errores.La din´mica de la pila, es decir, la manera en c´mo entran los datos a la a oestructura de datos y c´mo salen, se denomina fifo, que viene del ing´s first o ein first out (primero en entrar, primero en salir). Figura 12. Din´mica de la pila P aEn la figura 12 se muestran “fotograf´ ıas” en distintos momentos de la pila,cuando se desea insertar H justo debajo de F. Para hacer esto se requiere,retirar tantos elementos como sean necesarios, aqu´ se han retirado de la cima ıG y F para luego insertar H, que quedar´ posteriormente debajo de F. aLo que sucede es que, cuando se retira el elemento G se debe hacer una evalu-aci´n para determinar si el elemento retirado es el elemento objetivo, en este ocaso el elemento objetivo es F, puesto que se desea insertar un elemento debajode F.Despu´s de haber insertado F, insertamos de nuevo los elementos F y G en ese eorden, adem´s de insertar finalmente el elemento I que queda en la cima de la apila. Enseguida veremos con m´s detalle las operaciones b´sicas de las pilas. a a 23
  24. 24. 2.2. Operaciones b´sicas aLas operaciones b´sicas de una pila son: a1. En la pila S, insertar un elemento e: push(S,e),2. Retirar un elemento de la pila S: pop(S),3. Verificar si la pila S est´ vac´ stackempty(S) y a ıa:4. Saber cu´l es el elemento en la cima de la pila S: stacktop(S). aenseguida cada una de estas operaciones:2.2.1. La operaci´n push oEsta operaci´n sirve para insertar un elemento e en la pila S, lo vamos a oescribir como:push(S,e)Despu´s de hacer esta operaci´n sucede que: e o El elemento en la cima de la pila S ahora es e2.2.2. La operaci´n pop oPara retirar un elemento de la pila S y asignarlo a una variable del mismo tipoque el tipo de los elementos de la pila, usaremos la operaci´n pop escribi´ndola o ecomo:v=pop(S);En donde v es una variable que almacena el valor del elemento que estaba enla cima de S. Hacer esta operaci´n tiene algunas implicaciones: o La variable v debe ser del mismo tipo que los elementos almacenados en la pila. Solamente se puede retirar un elemento de la pila a la vez. Antes de la operaci´n, e era el elemento en la cima, ahora ya no lo es m´s. o a El apuntador “cima” decrece en una unidad.2.2.3. La operaci´n stackempty oEsta operaci´n toma como argumento una estructura del tipo stack (pila) y odevuelve un valor booleano, devuelve un true si la pila est´ vac´ y devuelve a ıa 24
  25. 25. un false si la pila tiene al menos un elemento, es decir:   true si S tiene 0 elementos  stackempty(S) =  f alse si S tiene m´s de 0 elementos  a2.2.4. La operaci´n stacktop oLa operaci´n stacktop(S) devuelve el valor del elemento en la cima de la pila oS. Para hacer esta operaci´n escribiremos: ov=stacktop(S)las implicaciones de usar esta operaci´n son: o Se hace una copia del elemento que est´ en la cima a En realidad se hacen dos operaciones, primero se hace v=pop(S), luego un push(S,v), porque despu´s de la operaci´n stacktop, la pila S queda sin e o cambio alguno.2.3. Ejemplo: N´mero de par´ntesis u eSupongamos ahora la expresi´n ((5+6)*4)/(17+9), una de las condiciones opara que sea una expresi´n aritm´tica correcta en que tengas sus par´ntesis o e ebalanceados, as´ que deseamos saber si el n´mero de par´ntesis que abres es ı u eel mismo n´mero de par´ntesis que cierran. u ePara resolver este problema usaremos el concepto de pila. La idea es simple.Vamos a leer cada elemento de la expresi´n, si se trata de un par´ntesis que o eabre, entonces lo insertaremos en una pila; si se trata de un par´ntesis que ecierra, entonces sacamos un elemento de la pila. Al terminar de leer la expre-si´n revisaremos si la pila est´ vac´ en cuyo caso habremos conclu´ que el o a ıa, ıdon´mero de par´ntesis que abre es el mismo que el n´mero de par´ntesis que u e u ecierra y la expresi´n tiene par´ntesis balanceados. o eVeamos c´mo funciona: o‘(’ : push(S,‘(’)‘(’ : push(S,‘(’)‘5’ : nada que hacer‘+’ : nada que hacer‘6’ : nada que hacer‘)’ : v=pop(S) 25
  26. 26. ‘*’ : nada que hacer‘4’ : nada que hacer‘)’ : v=pop(S)‘/’ : nada que hacer‘(’ : push(S,‘(’)‘17’: nada que hacer‘+’ : nada que hacer‘9’ : nada que hacer‘)’ : v=pop(S)Empezamos con un contador iniciado en 0, y por cada push aumentamos uncontador, y por cada pop decrementamos el contador. Al final vemos el valordel contador, si el contador=0 entonces terminamos con ´xito, de otro mod ese˜alamos el error. nEn la figura 13 se muestra la actividad de la pila a medida que se van agregandoy quitando elementos. Figura 13. Evaluaci´n del balance de par´ntesis en una expresi´n aritm´tica o e o e2.4. La estructura de datos Pila en C/C++Una pila est´ conformada por dos elementos: a Un espacio suficientemente grande para almacenar los elementos insertados en la pila Una parte que nos se˜ale cu´l es el elemento en la cima de la pila. n aEstas partes las conformamos en una estructura, descrita como sigue:definir numero maximo de elementos en la piladefinir nuevo tipo estructura llamado "stack" con item : un arreglo de 1 a maximos elementos enteros top : un numero de 0 a maximos elementos 26
  27. 27. fin de la nueva estructuraF´cilmente podemos describir un c´digo en C/C++ que represente lo anterior- a omente propuesto.// En la parte de definiciones#define maxElem 100// En la parte de tiposstruct stack { int item[maxElem]; int top;};// En la parte de variablesstruct stack A;2.5. La representaci´n en C/C++ de las operaciones de una pila oEn esta secci´n veremos una implementaci´n de las cuatro operaciones b´sicas o o ade las pilas. Todas estas operaciones se han hecho desde un punto de vista deprogramaci´n funcional, sin duda se pueden describir en un modelo orientado oa objetos.2.5.1. La operaci´n push oEl siguiente segmento de c´digo ilustra c´mo se puede implementar la op- o oeraci´n insertar un elemento en una pila. Hemos supuesto que la pila ya oest´ definida como una estructura stack. a(1) void push(struct stack *S,int e){(2) S->top++;(3) S->item[S->top]=e;(4) }En la l´ınea (1) se observa que la operaci´n push recibe dos par´metros: la o adirecci´n de una estructura de tipo pila y un elemento de tipo entero. oLa l´ınea (2) incrementa el tope (cima) de la pila en una unidad, con el fin deagregar el elemento en una posici´n libre de la pila, lo cual se logra en la l´ o ınea(3), asignando el valor e en la casilla S->top del arreglo item de la pila. 27
  28. 28. 2.5.2. La operaci´n pop oLa operaci´n pop se escribe en forma de c´digo en C/C++ con la siguiente o osecuencia de ´rdenes: o(1) int pop(struct stack *S){(2) int valReturn;(3)(4) valReturn=S->item[S->top];(5) S->top--;(6) return valReturn;(7) }La l´ ınea (1) describe que esta funci´n devuelve un tipo entero, el tipo de oelementos guardados en la pila; luego notamos que debemos dar s´lo la direc- oci´n de alguna variable de tipo estructura de pila (struct stack *). Obtener la odirecci´n se logra con el operador de indirecci´n (&). o oLas l´ ıneas (4) y (5) hacen todo el trabajo de esta funci´n, se almacena el valor oque ser´ devuelto en una variable de tipo entero y luego se decrementa el tope ade la pila.2.5.3. La operaci´n stackempty oLa operaci´n stackempty se describe en el siguiente segmento de c´digo: o o(1) bool stackempty(struct stack *S){(2) bool valorDevuelto;(3) if(S->top== -1)(4) valorDevuelto=true;(5) else(6) valorDevuelto=false;(7) return valorDevuelto;(8) }El encabezado de la funci´n que se muestra en la l´ o ınea (1) establece quese devuelve un valor booleano, y que se debe dar un par´metro, que es la adirecci´n de una localidad de memoria que almacena una estructura de tipo opila. El objetivo de esta funci´n es claro: oLa l´ ınea (3) establece la verdacidad o falsedad del predicado (S->top==-1),determinando si el nivel del tope es igual que -1, en cuyo caso devuelve unverdadero (4), de otro modo ha de devolver un valor falso (6). Se ha establecidoun -1 como vac´ porque el manejo de arreglos en C/C++ empieza en el ´ ıo ındice0, que a diferencia de otros lenguajes como Pascal, empiezan en 1. 28
  29. 29. 2.5.4. La operaci´n stacktop oEste es un caso especial porque no se requiere hacer ning´n c´digo. u oEsta funci´n debe devolver un n´mero entero y dejar la pila sin cambio. Para o ulograr esto se debe hacer un pop(&A), mostrar el elemento y luego insertar denuevo el elemento en la pila haciendo un push(&A,elemento), notemos que sehan usado los operadores de direcci´n para dar la direcci´n de la variable que o oalberga una estructura de tipo pila. El siguiente segmento de c´digo ilustra oc´mo se han usado las funciones antes creadas, por supuesto que se pueden oseparar y crear una nueva funci´n que haga lo mismo: o ...(1) case 4:{(2) if(not stackempty(&A)){(3) valor=pop(&A);(4) std::cout<<"La cima de la pila es: "<<valor<<"n";(5) push(&A,valor);(6) } else(7) std::cout<<"La pila esta vacia";(8) break;(9) } ...2.6. Problemas de programaci´n oLos siguientes ejercicios deben ser resueltos en un progr´ma (en C/C++ ): a1. Expresiones entrefijas y prefijas. Las expresiones aritm´ticas pueden e representarse de varias maneras, una de ellas, la m´s usual es la notaci´n a o entrefija. La notaci´n entrefija establece que en medio de dos operandos se escribe o un operador, como por ejemplos: a) a ∗ b, donde los operandos son a y b, y el operador es el s´ımbolo ∗; b) 2 + 5 ∗ ((5 + 7)/4) Donde el par´ntesis m´s interno establece la may- e a or prioridad, de manera que primero se debe evaluar (5 + 7), luego (12/4), luego 2 + (5 ∗ 3) y fimalmente (2 + 15), dando como resultado 17. c) −1 No hay nada que hacer, pues es un operador unario. En las expresiones prefijas se establece que el orden de escritura debe ser, primero el operador y luego la lista de operandos: a) ∗ab, donde los operandos son a y b, y el operador es el s´ ımbolo ∗; b) +2 ∗ 5/ + 574 Lo primero que hay que hacer es tomar el primer 29
  30. 30. operador y tomar los operandos necesarios siguientes (dos si se trata de un operador binario y uno si es un operador unario). En este caso se trata de evaluar 2 + [∗5/ + 574]. Cada uno de los operandos debe ser tratado de nuevo como una expresion en prefijo, de manera que se repite lo anterior, tomar el operador y la lista de sus operandos y tratar cada uno de sus operandos como expresiones en prefijo: 2 + [5 ∗ [/ + 574]], luego 2 + [5 ∗ [[+57]/4]] y finalmente 2 + [5 ∗ [[5 + 7]/4]] y evaluar. Los par´ntesis cuadrados son para ilustrar el ejemplo y no e son necesarios para su evaluaci´n. o c) −1 No hay nada que hacer, pues es un operador unario. Haga un programa en C/C++ que transforme expresiones de entrefijo a prefijo, y de prefijo a entrefijo. Los caracteres v´lidos son: las letras a may´sculas y min´sculas, los n´meros enteros, los par´ntesis normales, u u u e los cuatro operadores (+, −, ∗, /, ) y el operador unario (−). Figura 14. Ilustraci´n del estacionamiento mencionado en el problema 2 o2. en cierto punto de la ciudad hay un estacionamiento como el que se muestra en la figura 14, en donde hay lugar para 9 veh´ ıculos. haga un programa que muestre el manejo de este estacionamiento, considerando los siguientes requisitos: a) Los veh´ ıculos proporcionan la siguiente informaci´n: Placas (6 digi- o tos), Estado (2-3 caracteres, p.e. SON, DF, CHI, YUC), Marca, Mod- elo, A˜o-Modelo, Nombre del propietario. n b) Al llegar un ve´ ıculo se acepta solamente si hay lugar disponible. c) Validar todas las operaciones de la pila. d ) En cualquier momento se puede sacar alg´n veh´ u ıculo del estacionamien- to, regresando los veh´ ıculos en el orden en que estaban. e) Toda la corrida del programa debe hacerse hacia/desde la terminal est´ndar. a3. Haga un programa que implemente 2 pilas en 1 arreglo A[1..n] de man- era que ninguna pila se desborde a menos que el n´mero de elementos en u ambas pilas sea n 30
  31. 31. 3. ColasDefinici´n 5 Las colas son una estructura de datos similar a las pilas. Recorde- omos que las pilas funcionan en un dep´sito en donde se insertan y se retiran oelementos por el mismo extremo. En las colas sucede algo diferente, se inser-tan elementos por un extremo y se retiran elementos por el otro extremo. Dehecho a este tipo de dispositivos se les conoce como dispositivos “fifo” (firstin, first out) porque funcionan como una tuber´ lo que entra primero por un ıa,extremo, sale primero por el otro extremo.En una cola hay dos extremos, uno es llamado la parte delantera y el otroextremo se llama la parte trasera de la cola. En una cola, los elementos seretiran por la parte delantera y se agregan por la parte trasera.Figura 15. Din´mica de una cola. a) estado actual con una cola con tres elementos aa,b,c; b) estado de la cola cuando se agrega el elemento d; c) estado de la colacuando se elimina el elemento a del frente de la colaEn la figura 15 se muestra una actividad t´ ıpica de la cola, en donde se muestraque se agregan datos por la parte trasera de la cola y se eliminana datos porel frente de la cola.Si Q es una cola y x es un elemento, se pueden hacer tres operaciones b´sicas acon las colas:1. insert(Q,x), que inserta el elemento x en la parte trasera de la cola Q.2. x=remove(Q), que almacena en x el valor del elemento retirado de la parte frontal de la cola Q.3. empty(Q), que es un predicado de valor booleano, y es verdadero cuando la cola Q tiene 0 elementos, y es f also cuando la cola Q tiene al menos un elemento, en cuyo caso, ese unico elemento es la parte frontal y la parte ´ trasera de la cola al mismo tiempo. 31
  32. 32. Te´ricamente no hay l´ o ımite para el tama˜o de la cola, asi que siempre se ndeber´ poder insertar elementos a una cola, sin embargo, al igual que las ıapilas, normalmente se deja un espacio de memoria para trabajar con estaestructura. Por el contrario, la operaci´n remove s´lamente se puede hacer si o ola cola no est´ vac´ a ıa.3.1. Estructura de las colas en C/C++De manera similar a las pilas, las colas definen una estructura no est´ndar, de amanera que se debe crear un nuevo tipo de dado, el tipo cola, que debe tenerlos siguientes elementos: Un arreglo de n elementos de alg´n tipo espec´ u ıfico, puede incluso ser un tipo est´ndar o no. a Un n´mero que indica el elemento que est´ en la posici´n del frente de la u a o cola. Un n´mero que indica el elemento que est´ en la posici´n trasera de la cola. u a oSuponiendo que los elementos son n´meros enteros, una idea para representar uuna cola en C/C++ es usar un arreglo para contener los elementos y emplearotras dos variables para representar la parte frontal y trasera de la cola.#define maxQueue 100struct cola{int items[maxQueue];int front;int rear;};Esta representaci´n con arreglos es completamente v´lida, pero debemos tener o acuidado con los l´ ımites del arreglo. Suponiendo que no existiera la posibilidadde caer en un desbordamiento del arreglo, es decir, que se insertaran m´s aelementos de lo que el arreglo puede almacenar, la operaci´n insert podr´ o ıaquedar como:void insert(struct cola *C, int e){C->items[++C->rear]=e;}y al operaci´n x=remove(Q) oint remove(struct cola *C){return C->items[C->front++]; 32
  33. 33. }y finalmente la operaci´n empty(Q): obool empty(struct cola *C){if(C->front>C->rear)return true;elsereturn false;}3.2. Colas con prioridadUna cola con prioridad es una estructura de datos en la que se ordenan losdatos almacenados de acuerdo a un criterio de prioridad. Hay dos tipos decolas de prioridad: Las colas de prioridad con ordenamiento descendente. Las colas de prioridad con ordenamiento ascendente.En las colas de prioridad ascendente se pueden insertar elementos en formaarbitraria y solamente se puede remover el elemento con menor prioridad. SiCPA es una cola de prioridad ascendente, la operaci´n insert(CPA,x) inserta oel elemento x en la cola CPA; y la operaci´n x=minRemove(CPA) asigna a x el ovalor del elemento menor (de su prioridad) y lo remueve de la cola.En las colas de prioridad descendente es similar, pero s´lo permite la supresi´n o odel elemento m´s grande. Las operaciones aplicables a la cola de prioridad adescendente son insert(CPD,x) y x=maxRemove(CPD), cuando CPD es unacola de prioridad descendente y x es un elemento.La operaci´n empty(C) se aplica a cualquier tipo de cola y determina si una ocola de prioridad est´ vac´ Las operaciones de insertar y borrar se aplican a ıa.solamente si la pila no est´ vac´ a ıa.Los elementos de la cola de prioridad no necesitan ser n´meros o caracteres upara que puedan compararse directamente. Pueden ser estructuras complejasordenadas en uno o varios campos. Por ejemplo, las agendas telef´nicas constan ode apellidos, nombres, direcciones y n´meros de tel´fono y est´n ordenadas u e apor apellido.A diferencia de las pilas y las colas, en las colas de prioridad se pueden sacarlos elementos que no est´n en el primer sitio del extremo donde salen los aelementos. Esto es porque el elemento a retirar puede estar en cualquier parte 33
  34. 34. del arreglo.Cuando se requiere eliminar un dato de una cola de prioridad se necesitaverificar cada uno de los elementos almacenados para saber cu´l es el menor a(o el mayor). Esto conlleva algunos problemas, el principal problema es que eltiempo necesario para eliminar un elemento puede crecer tanto como elementostenga la cola.Para resolver este problema hay varias soluciones:1. Se coloca una marca de “vac´ en la casilla de un elemento suprimido. ıo” Este enfoque realmente no es muy bueno, porque de cualquier modo se accesan los elementos para saber si es una localidad vac´ o no lo es. Por ıa otro lado, cuando se remueven elementos, se van creando lugares vac´ ıos y despu´s es necesario hacer una compactaci´n, reubicando los elementos e o en el frente de la cola.2. Cada supresi´n puede compactar el arreglo, cambiando los elementos o depu´s del elemento eliminado en una posici´n y despu´s decrementando e o e rear en 1. La inserci´n no cambia. En promedio, se cambian la mitad de o los elementos de una cola de prioridad para cada supresi´n, por lo que o esta operaci´n no es eficiente. o3.3. Ejercicio de programaci´n o1. Modifique los procedimientos de insertar, retirar y verificar-cola-vac´ ıa para que considere aprovechar los espacios dejados al retirar elementos.2. Un deque es un conjunto ordenado de elementos del cual pueden elimi- narse elementos en cualquier extremo y en el cual pueden insertarse ele- mentos en cualquier extremo. Llamemos a los dos extremos de un deque left (izquierdo) y right (derecho). ?’c´mo se representa un deque en o un arreglo en C/C++ ? escriba un programa que maneje un deque, y que considere las cuatro rutinas removeLeft removeRight insertLeft insertRight para remover e insertar elementos en los extemos izquierdo y derecho de un deque. Aseg´rese de que las rutinas funcionan adecuadamente para u que un deque vac´ y que detectan desbordamiento y subdesbordamiento. ıo3. Programe las colas de prioridad ascendente y descendente.4. Existe un estacionamiento que tiene un s´lo carril que aloja hasta 10 o 34
  35. 35. carros. Los autos llegan por el extremo sur del estacionamiento y salen porel extremo norte del mismo. Si llega un cliente para recoger un carro queno est´ en el extremo norte, se sacan todos los autom´viles de ese lado, a ose retira el auto y los otros coches se restablecen en el mismo orden queestaban. Cada vez que sale un auto, todos los autos del lado sur se muevenhacia adelante para que en todas las ocasiones todos los espacios vac´ ıosest´n en la parte sur del estacionamiento. Escriba un programa que lea un egrupo de lineas de ingreso. Cada l´ ınea contiene una “A” para las llegadasy una “D” para las salidas y un n´mero de placa. Se supone que los ucarros llegan y salen en el orden especificado en la entrada. El programadebe imprimir (en la terminal est´ndar) un mensaje cada vez que entra ao sale un auto. Cuando llega un carro, el mensaje debe especificar si hayespacio o no para ´l en el estacionamiento. Si no hay espacio, el carro eespera hasta que hay espacio o hasta que se lee una l´ ınea de salida parael auto. Cuando queda disponible espacio, debe imprimirse otro mensaje.Cuando salga un coche, el mensaje debe incluir la cantidad de veces quese movi´ el auto dentro del estacionamiento, incluyendo la salida misma, opero no la llegada. Este n´mero es 0 si el carro sale de la fila de espera. u 35
  36. 36. 4. Recursi´n oUn tema fundamental para los pr´ximos temas es el de recusri´n. La recursi´n o o oes muy importante tanto en mate´ticas como em computaci´n, pues se usa a orecursi´n para definir procedimientos autosimilares. oDefinici´n 6 Decimos que un objeto es recursivo si en su definici´n se nom- o obra a s´ mismo. ıEn programaci´n, una funci´n es recursiva si en el ´mbito de esa funci´n hay o o a ouna llamada a s´ misma, C/C++ permite esta clase de acciones. Los algoritmos ırecursivos dan elegancia a las soluciones de los problemas. Un ejemplo cl´sico aes el factorial de un n´mero. uUna manera de definir el factorial de un n´mero n > 1 es: u n !n = i, i=1es decir, el producto de todos los n´meros enteros menores o guales que ´l, lo u eque se puede resolver f´cilmente con una funci´n iterativa, esto es, una funci´n a o ocon un ciclo que itere suficientes veces, incrementando un valor y entonces iralmacenando en una variable el resultado de esas multiplicaciones.Una implementaci´n de esta definici´n iterativa es: o o(1) int i,n;(2) long double valorAc;(4) valorAc=1.0;(5) std::cout << "Numero entero:";(6) std::cin>> n;(7) for(i=1; i<=n; i++) valorAc = valorAc*i;(8) std::cout<<"El factorial de "<<n<<" es:"<<valorAc;El ciclo principal es en la l´ınea (7). No hay ning´n truco hasta aqu´ La u ı.unica observaci´n importante es en la l´´ o ınea (2) en donde se declara el tipolong double para el valor del resultado, la raz´n para tal acci´n es que el o on´mero factorial crece muy r´pido y a´n con entradas en el rango de los u a ucaracteres (hasta 255), el factorial es muy grande. Este procedimiento com-putacional no hace uso de t´cnicas especiales empleadas para tratar n´meros e ugrandes.Sin embargo una soluci´n m´s elegante es usar la definici´n recursiva, y esta o a oes: 36
  37. 37. !n = n ∗ !(n − 1)El programa en C/C++ es el que se muestra a continuaci´n: o( 1) double factorial(double a){( 2) if (a<=1) return 1.0;( 3) else return (a *factorial(a-1.0)); }( 4)( 5) int main (int argc, char * const argv[]) {( 6) double n;( 7) std::cout << "Numero entero:";( 8) std::cin>> n;( 9) std::cout<<"El factorial de "<<n<<" es: "<< factorial(n);(10) return 0; }Aqu´ hay varias cosas que se˜alar, en primer lugar se ha creado una nueva ı nfunci´n, a diferencia de la definici´n iterativa en donde era suficiente traba- o ojar en el programa principal. Esta funci´n se llama factorial (como era de osuponerse), y empieza su encabezado en la l´ ınea (1).All´ mismo en la misma l´ ı ınea (1), es de notar que hemos emplado ahora eltipo double tanto para el tipo devuelto como para el tipo del argumento, adiferencia de la versi´n iterativa en donde emple´bamos tipos diferentes. La o araz´n es que al iniciar la recursi´n el argumento es del tipo devuelto, asi que o odeben ser del mismo tipo.Cada llamada recursiva genera una entrada a una pila, en donde se guardan(como elementos) los estados generales del sistema al momento de hacer lallamada, entonces, cuando se termina la funci´n se recupera una entrada de la opila. En la figura 16 ilustra c´mo funciona la recursividad cuando se intenta oobtener el factorial(5). Figura 16. Recursividad cuando se ejecuta factorial(5) 37
  38. 38. 4.0.1. La serie FibonacciUna de las series m´s famosas es sin duda alguna la serie de Fibonacci: a 1, 1, 2, 3, 5, 8, 13, 21, 34, . . .Un poco de observaci´n es sufucuente para encontrar que cualquier n´mero o u(a partir del tercero de la serie, osea el segundo 1) es igual a la suma de losdos n´meros anteriores. uDaremos en primer lugar la versi´n iterativa. En este algoritmo deseamos oencontrar el n-´simo n´mero de la serie Fibonacci. As´ si n = 4 el resultado e u ıdel algoritmo debe ser 3; si n = 6 el resultado debe ser 8. La versi´n iterativa oempieza desde los primeros 1’s, sum´ndolos y encontrando el tercero, luego apara encontrar el cuarto n´mero se suman el tercero (reci´n encontrado) y el u esegundo, y as´ en adelante hasta encontrar el n´mero buscado. ı u#include <iostream>int main (int argc, char * const argv[]) {int i,n,fib,fib1,fib2,fibx;std::cout<<"Un numero entero:";std::cin>>n;fib1=2; fib2=1; i=3;if((n==1)||(n==2))fib=1;else{do{fib = fib1 + fib2;fibx = fib1; i++;fib1 = fib; fib2 = fibx;}while(i<n);}std::cout << "nEl "<<n<<"-esimo numero de la serie Fibonacci es: "<<fib;return 0;}La definici´n recursiva para encontrar todos los n primeros n´meros de la serie o uFibonacci es: 38
  39. 39.  1  Si n = 1 ´ n = 2 o fib(n) =  fib(n − 1) + fib(n − 2) Si n > 2 En el siguiente c´digo, la soluci´n que propone la recursividad resulta en una o oprogramaci´n elegante, aunque costosa. El c´digo que hace esto es: o o( 1) #include <iostream>( 2) //====================( 3) int fib(int val){( 4) if ((val==1)||(val==2))( 5) return 1;( 6) else( 7) return (fib(val-1)+fib(val-2));( 8) }( 9) //====================(10) int main (int argc, char * const argv[]) {(11) int n;(12) std::cout<<"Numero entero:"; std::cin>>n;(13) std::cout<<"nEl "<< n(14) <<"-esimo numero fibonacci es: "<< fib(n);(15) return 0;(16) }Como regla general, cualquier algoritmo recursivo se puede reescribir en unalgoritmo iterativo. La ventaja de tener un algoritmo iterativo es que no seusa una pila para guardar llamadas a la misma funci´n de manera recursiva, oesto es una ventaja porque el espacio de memoria destinado al uso de la pila esgeneralmente limitado, de manera que cuando se hacen demasiadas funcionespush seguramente llegar´ el momento en que la pila “se desborde”, que por acierto es un t´rmino usado en computaci´n para decir que ya no hay m´s e o aespacio disponible en la pila.4.1. Peligros en la recursividadEl principal peligro al usar recursividad, es no tener una manera de salir delpaso recursivo, esto es peligroso porque se hacen llamadas a la misma funci´n, olo que significa una entrada en la pila donde se almacenan los estados generalesdel programa.Para decidir hacer un programa recursivo se deben de tener al menos dos cosasmuy claras: 39
  40. 40. 1. El paso base: Esta es la clave para terminar la recursi´n, es cuando deja o de hacer llamadas a la funci´n recursiva y hace evaluaciones devolviendo o los resultados. En el ejemplo de la serie de Fibonacci, el paso base est´ en a la l´ ınea ( 5). Adem´s se debe asegurar de que es posible entrar a este paso. a2. El paso recursivo: Es la parte de la definici´n que hace llamadas a o esa misma funci´n y que es la causante de las inserciones en la pila, o almacenando en cada una de las llamadas, informaci´n del programa, del o estado de sus variables locales y globales. En el mismo ejemplo de la serie Fibonacci, el paso recursivo se muestra en la l´ınea ( 7).Otras cosas que se deben tener claras son por ejemplo si se pasa una variablecomo referencia o por valor, si las variables apuntadores son del tipo adecuadoetc.Frecuentemente tanto el paso base como el paso recursivo, se encuentran enuna sentencia condicional if, pero porsupuesto que es posible usar cualquierotra sentencia de control, dependiendo de las necesidades particulares del prob-lema.El siguiente ejemplo ilustra este problema( 1) #include <iostream>( 2) int malaFuncion( int n ){( 3) std::cout << "malaFuncion es una recursion infinita. n="<<n;( 4) if( n == 0 )( 5) return 0;( 6) else( 7) return malaFuncion( n / 3 + 1 ) + n - 1;( 8) }( 9) int main (int argc, char * const argv[]) {(10) std::cout << malaFuncion(10);(11) return 0;(12) }4.2. Ejercicios de programaci´n oLos siguientes ejercicios deben de ser programados en C/C++ :1. B´ squeda binaria: Considere un arreglo de elementos (n´meros enteros u u est´ bien) en el cual los objetos ya estan ordenados, y se desea encon- a trar un elemento dentro de este arreglo. Es decir, se desea realizar una “b´squeda”. u La idea general de este m´todo de b´squeda binaria es: e u Si el arreglo tiene 1 elemento, se compara con el numero requerido y la 40
  41. 41. b´squeda termina. u Si el arreglo tiene m´s de 1 elemento, tendremos que dividir en dos el a arreglo y decidir en qu´ parte del arreglo buscar; luego buscarlo usando e busqueda binaria2. Escriba un programa para calcular la cantidad de maneras diferentes en las cuales un entero n se puede expresar como la suma de dos enteros menores p < n y q < n tales que p + q = n 41
  42. 42. 5. ListasHay dos desventajas serias con respecto a las estructuras est´ticas de pilas y acolas usando arreglos. Estas desventajas son que tienen un espacio limitadode memoria y la otra desventaja es que es posible no ocupar toda la memoriadisponible, haciendo que se desperdicie espacio.Una soluci´n es usar listas. Las listas son estructuras de datos que son din´mi- o acas, esto significa que adquieren espacio y liberan espacio a medida que senecesita. sin embargo, hay una advertencia. Como regla general siempre hayque tener cuidado al manejar direcciones de espacios de memoria, porque esposible que accedamos a una localidad de memoria de la cual no deseabamoscambiar su contenido.Antes de estudiar las listas, daremos una breve introducci´n a los grafos, pues olas listas son un caso especial de los grafos.5.1. GrafosLos grafos son una manera visual de representar las relaciones.Definici´n 7 Si A y B son dos conjuntos, decimos que a ∈ A est´ relacionado o acon b ∈ B si es verdadera una sentencia R que considere a ambos elementos.Esta sentencia R puede ser cualquier predicado, por ejemplo: “es padre de”,“debe dinero a”, “toma el curso de” etc.; si el predicado es verdadero paraese par de elementos, lo escribimos como aRb, y si el predicado es falso, loescribimos como b .As´ los ejemplos citados, si a ∈ A, b ∈ B se puede leer: ı Si A es el conjunto de alumnos, B es el conjunto de materias y R es “toma el curso”, entonces pedroRlogica se lee “pedro toma el curso de logica. En la figura 17 se puede apreciar esto en forma de diagramas de Venn. Si A es el conjunto de personas y B es tambi´n el conjunto de personas, e y R es “debe dinero a”; marisolRrafaelle significa que “marisol debe dinero a rafaelle” y de ning´n modo es al contrario, es decir “rafaelle no u debe dinero a marisol”.Los elementos de la figura 17 definen un nuevo conjunto de elementos, elconjunto de pares de elementos que estan relacionados. As´ la relaci´n “toma ı oel curso de” es el siguiente: 42
  43. 43. Figura 17. Relaci´n “toma el curso de” para los conjuntos A de personas y B de omaterias. R = {(diana, programacion), (carolina, programacion), (carolina, compiladores), (carolina, lenguajes), (rafael, compiladores), (gustavo, lenguajes), (fabiola, lenguajes)}Gr´ficamente podemos ilustrar el conjunto R de “toma el curso de” con un agrafo como el que se muestra en la figura 18. Figura 18. Grafo que ilustra la relaci´n “toma el curso de”. oDe manera que podemos definir un grafo como una representaci´n gr´fica de o auna relaci´n. oDefinici´n 8 Para definir formalmente un grafo debemos establecer la sigu- oiente tupla: G = A, NDonde A es un conjunto de aristas y N = ∅ un conjunto no vac´ de nodos. ıoEn el caso de R, el conjunto A ∪ B es el conjunto de nodos y el conjunto deflechas es el conjunto de aristas. 43
  44. 44. Notemos que el conjunto A de aristas puede ser un conjunto vac´ pero de ıo,ning´n modo hay grafo sin nodos, es decir el conjunto N debe ser diferente uque el conjunto vac´ ıo.Supongamos ahora A = {1, 2, 3, 4, 5, 6} y la siguiente relaci´n en A: o R = {(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)}Esta relaci´n luce como aparece en la figura 20. o Figura 19. Relaci´n R de A en A oy en forma de grafo es: Figura 20. Grafo de la relaci´n R : A → A oA esta clase de grafos, en las que cada nodo tiene a lo m´s una arista dirigida aque sale y a lo m´s una arista dirigida que entra, se le llama lista. a5.2. Listas simplemente encadenadasComo vimos en la secci´n anterior, una lista es una relaci´n de elementos, tales o oque cada elemento est´ relacionado con unicamente un elemento del conjunto, a ´diferente a s´ mismo. ı 44
  45. 45. Como cada elemento puede tener a lo m´s una arista dirigida que sale y una aarista dirigida que entra, bien puede tener 0 aristas que salen, o cero aristasque entran. Si el nodo tiene 0 aristas que salen, entonces es el final de la lista.Si el nodo tiene 0 aristas que entran, entonces es el inicio de la lista.Por razones pr´cticas, se dibujan una flecha que sale de un identificador de la alista y entra al inicio de la lista y otra flecha que sale del final de la lista yapunta a un s´ımbolo que se llama NULO.Figura 21. Grafo de la relaci´n R : A → A con apuntadores del nombre de la lista olistaLigada y hacia NULLEn C/C++ el identificador de la lista contiene la direcci´n del primer elemento ode la lista, as´ como sucede con los arreglos. El valor NULO es util para saber ı ´cu´ndo termina la lista, es una constante est´ndar y no tiene valor. a aEl contenido de los nodos, como ya hemos visto, son los elementos de unconjunto. Si ese conjunto tiene elementos estructurados, tambi´n es v´lido e ausarlos.Normalmente cada nodo de la lista est´ estructurado con dos partes: a1. La parte de informaci´n. o2. La parte de direcci´n al siguiente nodo de la lista. oEl campo de informaci´n contiene el elemento real de la lista. El campo de odirecci´n al siguiente contiene un apuntador al elemento con el cu´l est´ rela- o a acionado, es decir, al elemento siguiente de la lista. La lista completa se accesamediante el identificador de lalista. El campo de la direcci´n del ultimo nodo o ´apunta a una direccion nula.La lista que no tiene elementos (solamente tiene un identificador que apunta anulo) se llama lista nula o lista vac´ Una lista se inicializa a una lista vac´ ıa. ıahaciendo lista=null, recordemos que lista es un apuntador a una direcci´n ode memoria que puede albergar una variable del tipo que se hayan definidolos nodos; null es una direcci´n de cualquier tipo, as´ que el compilador asigna o ıla direcci´n null a lista. o 45
  46. 46. Enseguida vamos a dar una lista de t´rminos usados para manejar los elemen- etos de una lista simplemente encadenada, aunque no son los que usa C/C++ ,pero s´ son bastante claros para hacer algoritmos. Si p es un apuntador a la ıdirecci´n de una variable del tipo declarado para los nodos de una lista: onode(p): hace referencia al noso al que se apunta mediante p.info(p): hace referencia a la informaci´n del nodo al que apunta p. onext(p): hace referencia a la parte direcci´n siguiente y, por tanto, es un o apuntador.As´ que la expresi´n info(next(p)) significa que se hace referencia a la secci´n ı o ode informaci´n del nodo siguiente al que apunta p. o5.2.1. Insertar y eliminar nodos de una listaEn el uso de las listas ligadas se ven involucradas varias operaciones, entreellas la de insertar un nuevo nodo a la lista y la operaci´n de eliminar un onodo de la lista. En ambos casos debemos recordar que se trata de manejo dela memoria, as´ que insertar un nodo en la lista significa obtener un espacio ıde memoria disponible y relacionarlo con los elementos de la lista; as´ mismo, ıeliminar un nodo de la lista significa liberar la memoria que ocupa ese nodosin perder la relaci´n con el resto de los nodos de la lista. oInsertar un elemento al inicio de la lista. La operaci´n p=getnode(); oobtiene un nodo vac´ y establece el contenido de una variable nombrada p en ıola direcci´n de este nodo, como se muestra en la figura 22.a. Este nodo a´n o uno pertenece a alguna lista, simplemente se ha logrado dedicar un especio dememoria que es apuntado por p, figura 22.b.Figura 22. a) Creaci´n de un nuevo nodo. b) El nuevo nodo debe de ir insertado al ofrente, atr´s o en medio de la lista. aUna vez que se ha creado un nuevo espacio para el nuevo nodo, se debe deestablecer la parte de informaci´n de ese nodo con la operaci´n info(p), como o ose ilustra en el siguiente ejemplo con el dato 6. 46
  47. 47. info(p)=6;Despu´s de esstablecer la parte de informaci´n es necesario establecer la parte e osiguiente de este nodo. Debido a que node(p) va a insertarse en la partedelantera de la lista, el nodo que sigue debe ser el primer nodo actual de lalista. Debido a que la variable lista (el identificador de la lista) contienela direcci´n de ese primer nodo, node(p) se agrega a la lista ejecutando la ooperaci´n onext(p)=lista;Esta operaci´n coloca el valor de lista (la direcci´n del primer nodo en la o olista) en el campo siguiente de node(p). Estos pasos se ilustran en la figura23Figura 23. Operaciones involucradas en la inserci´n de un nuevo nodo al inicio de ouna lista: c) info(p). d) next(p)=list. e) list=pHasta ahora, p apunta a la lista con el elemento adicional incluido. Sin em-bargo, debido a que list es el apuntador externo a la lista deseada, su valordebe modificarse en la direcci´n del nuevo primer nodo de la lista. Esto se ohace ejecutando la operaci´n olist=p;En resumen, ya tenemos un algoritmo para insertar un elemento al inicio deuna lista simplemente ligada, al reunir todos los pasos tenemos:p=getnode();info(p)=6; 47
  48. 48. next(p)=list;list=p;Eliminar un elemento de la lista. Para eliminar un elemento del iniciode la lista, se siguen los mismos pasos que se usan para insertar un elemento,pero en un orden diferente:p=list;x=info(p);list=next(p);Comentaremos cada una de estas tres l´ ıneas, que se pueden apreciar en lafigura 24Figura 24. Operaciones involucradas en la eliminaci´n de un nodo al inicio de una olista: c) p=list). d) x=info(p). e) list=next(p)5.2.2. Listas en C/C++ con arreglosVamos a empezar una primera implementaci´n de listas usando arreglos, cada oelemento del arreglo debe ser un elemento compuesto. Cada elemento debecontener una parte para la informaci´n y otra parte para apuntar al elemento osiguiente:#include <iostream>( 1) #define numNodes 500( 2) struct nodeType{( 3) int info; 48
  49. 49. ( 4) int next;( 5) };( 6) struct nodeType node[numNodes];int main (int argc, char * const argv[]) { std::cout << "Hello, World!n"; return 0;}En el programa anterior, en las l´ ıneas (2) a (5) se crea un nuevo tipo de dato,el tipo nodo. Cada nodo tiene dos partes, su parte de informaci´n y su parte ode apuntador al siguiente. Como solamente tenemos 500 nodos (declarados enla l´ ınea (1), el tipo de siguiente es entero y hemos decidido almacenar n´meros uenteros solamente.En la l´ ınea (6) se ha declarado una variable global de tipo arreglo de estructurade nodos, es decir, se ha creado un arreglo de 500 nodos.En este esquema, el ultimo nodo apunta a NULL, que se representa con ´el valor entero -1. Tenemos tambi´n los siguientes elementos de cada nodo: enode[p] corresponde a next(p), por la notaci´n propia del lenguaje; tambi´n o enode[p].info para info(p) y finalmente node[p].next hace referencia alnodo siguiente next(p).Al principio todos los nodos est´n sin usar, porque solamente se ha creado ael arreglo. As´ que todos los nodos van a formar parte de una lista de no- ıdos disponibles. Si se usa la variable global avail para apuntar a la listadisponible, podr´ıamos organizar inicialmente esta lista como:void inicializaAvail(void){int i;avail = 0;for(i=0; i<numNodes-1; i++){node[i].next = i+1;}node[numNodes-1].next = -1;}Cuando se requiere un nodo para usarlo en la lista, se obtiene de la listadisponible. Cuando ya no es necesario ese nodo, se devuelve a la lista disponible.Estas dos operaciones se implementan mediante las rutinas en C/C++ getnodey freenode: 49
  50. 50. int getNode(void){ int p; if (avail==-1){ std::cout<<"Overflown"; exit(1); } p=avail; avail=node[avail].next; return p;}Si avail es igual a -1 significa que no hay nodos disponibles, es decir, que elarreglo est´ completamente lleno. Esto significa que las estructuras de lista ade un programa particular han desbordado el espacio disponible. La funci´n ofreeNode acepta un apuntador (n´mero entero) a un nodo y devuelve ese unodo a la lista de disponibles:void freeNode(int p){ node[p].next=avail; avail=p;}Las operaciones primitivas para listas son versiones directas en C de los al-goritmos correspondientes. La rutina insAfter acepta un apuntador p a unnodo y un elemento x como par´metros. Primero se asegura que p no sea nulo ay despu´s se inserta x en el nodo siguiente al indicado por p. evoid insAfter(int p, int x){ int q; if(p==-1){ std::cout<<"void insertionn"; } else{ q=getNode(); node[q].info=x; node[q].next=node[p].next; node[p].next=q; }}La rutina delAfter(p,px), llamada por el enunciado delAfter(p,&x), suprimeel nodo despu´s de node(p) y almacena su contenido en x; evoid delAfter(int p, int *px){ int q; if((p==-1)||(node[p].next==-1)){ 50

×