Escolar Documentos
Profissional Documentos
Cultura Documentos
HARDWARE - SOFTWARE
PRLOGO
La presente obra se diseo principalmente como un texto de referencia para los
estudiantes de Ingeniera de Sistemas e Informtica de la Universidad Privada de
Oruro, que necesitan de una gua prctica, proporcionndole informacin rpida y
objetiva sobre la metodologa a seguir en la codificacin de programas en C++ y la
Estructura de Datos.
Code::Blocks 10.05, un
utilizacin.
La Obra presente diversos problemas los mismos que han sido seleccionados no
tanto por su utilidad prctica en el campo de la solucin de problemas, sino por la forma y
complejidad que estos despliegan en el proceso de codificacin.
El texto cuenta con problemas que van desde lo ms bsico hasta los ms
complejos para que el programador pueda asimilar tanto el diseo de los problemas
codificados en forma gradual y sistemtica
10
HARDWARE - SOFTWARE
11
HARDWARE - SOFTWARE
short int:
unsigned int:
12
HARDWARE - SOFTWARE
float: El tipo de dato float, tiene un tamao de 32 bits, es usado comnmente en nmeros con 6
o menos cifras decimales. Tiene un rango entre 1.17549 e-38 hasta 3.40282 e+38.
double: El tipo de dato double, tiene un tamao de 64 bits, es usado para nmeros de menos de
15 cifras decimales. Tiene un rango entre 2.22507 e-308 hasta 1.79769 e+308.
long double: Tiene un tamao de 96 bits y una precisin de 18 cifras decimales. Tiene un rango
entre 3.3621 e-4932 hasta 1.18973 e+4932.
bool: El tipo de dato bool, tiene un tamao de 8 bits y un rango entre 0 y 1, en pocas palabras es
cero o es uno (falso o verdadero). Este tipo de dato, es comnmente usado en condicionales o
variables al que se le puede asignar las constantes true (verdadero) y false (falso).
char: Esta constituido por caracteres simples, como (a-z / A-Z / 0-9) y cadenas, como Esto es una
prueba (normalmente, de 8 bits o un byte por carcter, con un rango de 0 a 255).
Las variables del tipo char, son digamos las variables problema del lenguaje C y C++, puesto que
tienen una gran cantidad de restricciones y complicaciones, bastante molestas. Las variables de
tipo char, en C y C++ son consideradas vectores y como quiz sabrs a los vectores se les debe
declarar un tamao mximo, entre corchetes [ ] lo cual restringe un poco al momento de no
saber que tamao podra llegar a tener una cadena de caracteres, y aunque hay formas de evadir
esto, es bastante complicado, desde mi punto de vista personal recomiendo las variables de tipo
string para las cadenas de caracteres, estas estn incluidas en la librera #include <string.h> y son
bastante fciles de usar y muy flexibles a diferencia de los char. Muy bien, de igual forma pondr
como se debe declarar un char y ms abajo mencionare algunas otras molestias que este tipo de
dato genera, pero explicare como se arreglan.
Muy bien, la sintaxis es la siguiente:
char nombre_char [tamao_max];
Tambin es vlido y solo es vlido de esta forma la asignacin por medio del signo = para un
char y es as:
char char_nombre [tamao_max] = cadena;
Muy bien, como veras aqu lo nico que hay extrao es lo del [tamao_max], pues bien, ah se
debe poner un numero entero que no cambia es decir constante que nos indica el tamao
mximo que tendr nuestra cadena de caracteres, si nos pasamos de ese nmero, tendremos
problemas, as que recomiendo poner nmeros grandes si no se sabe el tamao que podra tener,
pero bueno, esto nos podra desperdiciar memoria y recursos o todo lo contrario, nos podran
quedar faltando.
13
HARDWARE - SOFTWARE
void: El tipo void, se utiliza para especificar valores que ocupan 0 bits y no tienen valor (este tipo
tambin se puede utilizar para la creacin de punteros genricos).
puntero: El tipo de dato puntero, no contiene informacin en el mismo sentido que el resto de
los tipos de datos; en su lugar, cada puntero contiene la direccin de la posicin de memoria que
almacena el dato actual.
1.3 CMO SE DECLARA UNA VARIABLE EN C++?
El sistema es siempre el mismo, primero se especifica el tipo y a continuacin una lista de
variables y finalmente un punto y coma.
La declaracin de variables es uno de los tipos de sentencia de C++.
La prueba ms clara de esto es que la declaracin terminar con un ";".
Sintaxis:
<tipo> <lista de variables>;
Declarara las variables a, seguir y encontrado; y adems iniciara los valores de a y seguir con
los valores 1234 y true, respectivamente.
1.4 REGLAS PARA LA DECLARACION DE VARIABLES
Bien pues, ya sabemos que en la programacin tenemos que estar muy atentos a la
sintaxis (Estructura de una Palabra), pues es importantsimo, ya que si no est bien escrito un
comando, no va a funcionar esa lnea en el cdigo del programa y peor an, si esa lnea est
ligada a las lneas de ms adelante pues no funcionar ni sta ni las de ms adelante (Como una
Cadena). Entonces les dir algunas reglas para la declaracin de variables, cuando veamos un
lenguaje de programacin especifico colocar la sintaxis de sus comandos ya que las sintaxis
cambia (La lgica no, sea cambian las palabras pero la forma de realizar el ejercicio sigue
siendo la misma), porque cada lenguaje tiene su dialecto. Comencemos:
14
HARDWARE - SOFTWARE
Regla 1:
Tener el mismo nombre que una palabra reservada del lenguaje.
Explicacin: Los lenguajes de programacin tienen palabras reservadas, sea que esas palabras
solo pueden ser usadas por el programa, por eso llevan el nombre de reservadas, pues si
supongamos el caso de que un lenguaje de programacin X tiene sus palabras reservadas entre
las cuales est: ingresar, entonces eso quiere decir que el usuario NO debe declarar una
variable con el nombre ingresar, porque va a tener conflictos ms adelante.
Regla 2:
Slo pueden ser letras, dgitos y el guin bajo subguin. Adems debe tener no ms de 40
caracteres.
Explicacin: Pues en los lenguajes de programacin hay sintaxis que deben cumplirse al pie de la
letra, entonces dice que las variables solo pueden llevar letras, nmeros y el subguin.
Ejemplo:
La siguiente variable est bien declarada: programando19
La siguiente variable est mal declarada:
%&programando-19
Vemos que insert caracteres especiales, adems de que uso el guin normal (no el subguin),
por lo tanto puede que el programa entienda que es una resta, entonces est mal declarado
por sintaxis.
Regla 3:
Deben comenzar por un carcter (letra).
Explicacin: Por sintaxis como ya hemos visto, deben cumplir con estas reglas, entonces no se
puede comenzar con un nmero, ya que se debe comenzar por una letra como dice la regla.
Ejemplo:
La siguiente variable est bien declarada: pasoApaso
La siguiente variable est mal declarada:
89pasos
Regla 4:
Deben iniciar con un carcter (no numero) como vimos en la Regla 3, y tambin puede comenzar
con un guin bajo (_).
15
HARDWARE - SOFTWARE
Ejemplo:
La siguiente variable est bien declarada: _descuento
La siguiente variable est mal declarada:
-descuento
descuento-
Regla 5:
No se les pueden asignar espacios en blanco.
Explicacin: Las variables no pueden llevar espacios en blanco, solo pueden ser separadas por un
signo dedicado a ser usado como un espacio, el cual es el subguin (_), entonces en una variable
cuando vean un subguin, prcticamente estn separando algo (para que no parezca una
ensalada).
Ejemplo:
La siguiente variable est bien declarada: eddy_19
La siguiente variable est mal declarada:
eddy 19
Regla 6:
No pueden llevar acento (tilde).
Ejemplo:
La siguiente variable est bien declarada: numero
La siguiente variable est mal declarada:
nmero
16
HARDWARE - SOFTWARE
auto
double
int
struct
break
else
long
switch
case
e num
register
typedef
char
extern
return
union
const
float
short
unsigned
continue
for
signed
void
default
goto
sizeof
volatile
do
if
static
while
17
HARDWARE - SOFTWARE
Estos objetos se utilizan mediante los operadores << y >>. El operador << se denomina operador
de insercin; y apunta al objeto donde tiene que enviar la informacin. Por lo tanto la sintaxis de
cout ser:
cout<<variable1<<variable2<<.........<<variableN;
No olvidemos que las cadenas de texto son variables y se ponen entre " " (comillas dobles).
Por su parte >> se denomina operador de extraccin, lee informacin del flujo cin (a la izquierda
del operador) y las almacena en las variables indicadas a la derecha).
La sintaxis sera la siguiente:
cin>>variable1>>variable2>>.....>>variableN;
Que mostrara por pantalla la frase "Introduce un nmero" y posteriormente almacenara el valor
introducido por teclado en la variable num, para luego mostrar por pantalla la frase Usted
introdujo: , seguidamente del dato almacenado en la variable num.
3. OPERADORES, EXPRESIONES Y SENTENCIAS.
3.1 OPERADORES.
Un operador es un carcter o grupo de caracteres que acta sobre una, dos o ms variables para
realizar una determinada operacin con un determinado resultado. Ejemplos tpicos de
operadores son la suma (+), la diferencia (-), el producto (*), etc. Los operadores pueden ser
unarios, binarios y ternarios, segn acten sobre uno, dos o tres operandos, respectivamente. En
C++ existen muchos operadores de diversos tipos (ste es uno de los puntos fuertes del lenguaje),
que se vern a continuacin.
3.1.1 OPERADORES ARITMTICOS
Los operadores aritmticos son los ms sencillos de entender y de utilizar. Todos ellos son
operadores binarios. En C++ se utilizan los cinco operadores siguientes:
18
HARDWARE - SOFTWARE
Suma: +
Resta: Multiplicacin: *
Divisin: /
Modulo: %
19
HARDWARE - SOFTWARE
Desde el punto de vista matemtico este ejemplo no tiene sentido (Equivale a 0 = 1!), pero s lo
tiene considerando que en realidad el operador de asignacin (=) representa una sustitucin; en
efecto, se toma el valor de variable contenido en la memoria, se le suma una unidad y el valor
resultante vuelve a depositarse en memoria en la zona correspondiente al identificador variable,
sustituyendo al valor que haba anteriormente. El resultado ha sido incrementar el valor de
variable en una unidad.
As pues, una variable puede aparecer a la izquierda y a la derecha del operador (=). Sin embargo,
a la izquierda del operador de asignacin (=) no puede haber nunca una expresin:
Tiene que ser necesariamente el nombre de una variable. Es incorrecto, por tanto, escribir algo
as como:
a + b = c; // incorrecto
Existen otros cuatro operadores de asignacin (+=, -=, *= y /=) formados por los cuatro
operadores aritmticos seguidos por el carcter de igualdad. Estos operadores simplifican algunas
operaciones recurrentes sobre una misma variable. Su forma general es:
variable op= expresion;
Donde op representa cualquiera de los operadores (+ - * /). La expresin anterior es
equivalente a:
variable = variable op expresion;
A continuacin se presentan algunos ejemplos con estos operadores de asignacin:
distancia += 1;
rango /= 2.0
x *= 3.0 * y - 1.0
equivale a:
equivale a:
equivale a:
distancia = distancia + 1;
rango = rango /2.0
x = x * (3.0 * y - 1.0)
20
HARDWARE - SOFTWARE
21
HARDWARE - SOFTWARE
expresion1 || expresion2
expresion1 && expresion2
El operador && devuelve un 1 si tanto expresion1 como expresion2 son verdaderas (o distintas
de 0), y 0 en caso contrario, es decir si una de las dos expresiones o las dos son falsas (iguales
a 0); por otra parte, el operador || devuelve 1 si al menos una de las expresiones es cierta. Es
importante tener en cuenta que los compiladores de C++ tratan de optimizar la ejecucin de
estas expresiones, lo cual puede tener a veces efectos no deseados. Por ejemplo:
Para que el resultado del operador && sea verdadero, ambas expresiones tienen que ser
verdaderas; si se evala expresion1 y es falsa, ya no hace falta evaluar expresion2, y de hecho no
se evala. Algo parecido pasa con el operador ||: si expresion1 es verdadera, ya no hace falta
evaluar expresion2.
Los operadores && y || se pueden combinar entre s quizs agrupados entre parntesis, dando
a veces un cdigo de ms difcil interpretacin. Por ejemplo:
(2==1) || (-1==-1)
(2==2) && (3==-1)
((2==2) && (3==3)) || (4==0)
((6==6) || (8==0)) && ((5==5) && (3==2))
// el resultado es 1
// el resultado es 0
// el resultado es 1
// el resultado es 0
22
HARDWARE - SOFTWARE
23
HARDWARE - SOFTWARE
x= (-b + sqrt((b*b)-(4*a*c)))/(2*a);
Donde, estrictamente hablando, slo lo que est a la derecha del operador de asignacin (=) es
una expresin aritmtica. El conjunto de la variable que est a la izquierda del signo (=), el
operador de asignacin, la expresin aritmtica y el carcter (;) constituyen una sentencia. En la
expresin anterior aparece la llamada a la funcin de librera sqrt(), que tiene como valor de
retorno la raz cuadrada de su nico argumento. En las expresiones se pueden introducir espacios
en blanco entre operandos y operadores; por ejemplo, la expresin anterior se puede escribir
tambin de la forma:
x= (-b + sqrt( ( b * b ) - ( 4 * a * c ) ) ) /(2*a);
24
HARDWARE - SOFTWARE
pueden emplear los operadores relacionales (<, >, <=, >=, ==, !=) para producir estos valores
lgicos a partir de valores numricos. Estas expresiones equivalen siempre a un valor 1 (true) o a
un valor 0 (false).
Por ejemplo:
a = ((b>c)&&(c>d))||((c==e)||(e==b));
Donde de nuevo la expresin lgica es lo que est entre el operador de asignacin (=) y el (;).
La variable a valdr 1 si b es mayor que c y c mayor que d, si c es igual a e e es igual a b.
25
HARDWARE - SOFTWARE
;
3.3.3 SENTENCIAS COMPUESTAS O BLOQUES
Muchas veces es necesario poner varias sentencias en un lugar del programa donde debera
haber una sola. Esto se realiza por medio de sentencias compuestas. Una sentencia compuesta es
un conjunto de declaraciones y de sentencias agrupadas dentro de llaves {...}. Tambin se
conocen con el nombre de bloques. Una sentencia compuesta puede incluir otras sentencias,
simples y compuestas. Un ejemplo de sentencia compuesta es el siguiente:
{
int i = 1, j = 3, k;
double masa;
masa = 3.0;
k = i + j;
}
26
HARDWARE - SOFTWARE
4.1.2 SENTENCIA IF
Esta sentencia de control permite ejecutar o no una sentencia simple o compuesta segn se
cumpla o no una determinada condicin. Esta sentencia tiene la siguiente forma general:
if (expresion)
sentencia;
Explicacin: Se evala expresion. Si el resultado es true (=1), se ejecuta sentencia; si el
resultado es false (=0), se salta sentencia y se prosigue en la lnea siguiente. Hay que recordar
que sentencia puede ser una sentencia simple o compuesta (bloque { ... }).
EJEMPLO N 1
Realizar un programa que permita determinar si un nmero introducido por teclado es positivo.
#include <iostream>
using namespace std;
int main()
{
int num;
cout<<"Introduzca un numero"<<endl;
cin>>num;
if (num>0)
cout<<"Es positivo";
return 0;
}
4.1.3 SENTENCIA IF ... ELSE
Esta sentencia permite realizar una bifurcacin, ejecutando una parte u otra del programa segn
se cumpla o no una cierta condicin. La forma general es la siguiente:
if (expresion)
sentencia_1;
else
sentencia_2;
Explicacin: Se evala expresion. Si el resultado es true (=1), se ejecuta sentencia_1 y se
prosigue en la lnea siguiente a sentencia_2; si el resultado es false (=0), se salta sentencia_1, se
ejecuta sentencia_2 y se prosigue en la lnea siguiente. Hay que indicar aqu tambin que
sentencia_1 y sentencia_2 pueden ser sentencias simples o compuestas (bloques { ... }).
EJEMPLO N 2
Disear un programa para determinar el mayor de dos nmeros introducidos por teclado.
27
HARDWARE - SOFTWARE
#include <iostream>
using namespace std;
int main()
{
int num1,num2;
cout<<"Introduzca el primer numero"<<endl;
cin>>num1;
cout<<"Introduzca el segundo numero"<<endl;
cin>>num2;
if (num1>num2)
cout<<"l mayor es: "<<num1;
else
cout<<"El mayor es: "<<num2;
return 0;
}
4.1.4 SENTENCIA IF ... ELSE MLTIPLE
Esta sentencia permite realizar una ramificacin mltiple, ejecutando una entre varias partes del
programa segn se cumpla una entre n condiciones. La forma general es la siguiente:
if (expresion_1)
sentencia_1;
else if (expresion_2)
sentencia_2;
else if (expresion_3)
sentencia_3;
else if (...)
...
else
sentencia_n;
Explicacin: Se evala expresion_1. Si el resultado es true, se ejecuta sentencia_1. Si el resultado
es false, se salta sentencia_1 y se evala expresion_2. Si el resultado es true se ejecuta
sentencia_2, mientras que si es false se evala expresion_3 y as sucesivamente. Si ninguna de
las expresiones o condiciones es true se ejecuta expresion_n que es la opcin por defecto (puede
ser la sentencia vaca, y en ese caso puede eliminarse junto con la palabra else). Todas las
sentencias pueden ser simples o compuestas. (bloques { ... }).
EJEMPLO N 3
Establecer si dos nmeros introducidos por teclado son consecutivos, ya sea en forma ascendente
o descendente.
#include <iostream>
using namespace std;
28
HARDWARE - SOFTWARE
int main()
{
int num1,num2;
cout<<"Introduzca un primer numero"<<endl;
cin>>num1;
cout<<"Introduzca un segundo numero"<<endl;
cin>>num2;
if (num2==(num1+1))
cout<<num1<<" "<<num2<<" Son numeros consecutivos";
else if (num1==(num2+1))
cout<<num1<<" "<<num2<<" Son numeros consecutivos";
else
cout<<num1<<" "<<num2<<" No son numeros consecutivos";
return 0;
}
4.1.5 SENTENCIA SWITCH
La sentencia que se va a describir a continuacin desarrolla una funcin similar a la de la
sentencia if ... else con mltiples ramificaciones, aunque como se puede ver presenta tambin
importantes diferencias. La forma general de la sentencia switch es la siguiente:
switch (expresion)
{
case expresion_cte_1: sentencia_1;
case expresion_cte_2: sentencia_2;
...
case expresion_cte_n: sentencia_n;
}
Explicacin: Se evala expresion y se considera el resultado de dicha evaluacin. Si dicho
resultado coincide con el valor constante expresion_cte_1, se ejecuta sentencia_1 seguida de
sentencia_2, sentencia_3, ..., sentencia. Si el resultado coincide con el valor constante
expresion_cte_2, se ejecuta sentencia_2 seguida de sentencia_3, ..., sentencia. En general, se
ejecutan todas aquellas sentencias que estn a continuacin de la expresion_cte cuyo valor
coincide con el resultado calculado al principio. Si ninguna expresion_cte coincide se ejecuta la
sentencia que est a continuacin de default. Si se desea ejecutar nicamente una sentencia_i (y
no todo un conjunto de ellas), basta poner una sentencia break a continuacin (en algunos casos
puede utilizarse la sentencia return o la funcin exit()). El efecto de la sentencia break es dar por
terminada la ejecucin de la sentencia switch. Existe tambin la posibilidad de ejecutar la misma
sentencia_i para varios valores del resultado de expresion, poniendo varios case expresion_cte
seguidos.
El siguiente ejemplo ilustra las posibilidades citadas:
switch (expresion)
{
case expresion_cte_1: sentencia_1;
break;
29
HARDWARE - SOFTWARE
EJEMPLO N 4
Realizar un men con las cuatro operaciones bsicas de la aritmtica.
#include <iostream>
using namespace std;
int main()
{
int a,b,op;
cout<<"Inserte primer valor: "<<endl;
cin>>a;
cout<<"Inserte segundo valor: "<<endl;
cin>>b;
cout<<endl<<"Seleccione una opcin: "<<endl;
cout<<"1.- Sumar"<<endl;
cout<<"2.- Restar"<<endl;
cout<<"3.- Multiplicar"<<endl;
cout<<"4.- Dividir"<<endl;
cout<<"5.- Salir"<<endl;
cout<<endl<<"Opcion: ";
cin>>op;
switch(op)
{
case 1: cout<<"La suma es: "<<a+b;break;
case 2: cout<<"La resta es: "<<a-b;break;
case 3: cout<<"El producto es: "<<a*b;break;
case 4: cout<<"La division es: "<<a/b;break;
case 5: break;
}
return 0;
}
4.1.6 SENTENCIAS IF ANIDADAS
Una sentencia if puede incluir otros if dentro de la parte correspondiente a su sentencia. A estas
sentencias se les llama sentencias anidadas (una dentro de otra). Por ejemplo:
if (a >= b)
if (b != 0.0)
c = a/b;
En ocasiones pueden aparecer dificultades de interpretacin con sentencias if...else anidadas,
como en el caso siguiente:
30
HARDWARE - SOFTWARE
if (a >= b)
if (b != 0.0)
c = a/b;
else
c = 0.0;
En principio se podra plantear la duda de a cul de los dos if corresponde la parte else del
programa. Los espacios en blanco las indentaciones de las lneas parecen indicar que la sentencia
que sigue a else corresponde al segundo de los if, y as es en realidad, pues la regla es que el else
pertenece al if ms cercano. Sin embargo, no se olvide que el compilador de C++ no considera los
espacios en blanco (aunque sea muy conveniente introducirlos para hacer ms claro y legible el
programa), y que si se quisiera que el else perteneciera al primero de los if no bastara cambiar
los espacios en blanco, sino que habra que utilizar llaves, en la forma:
if (a >= b)
{
if (b != 0.0)
c = a/b;
}
else
c = 0.0;
Recurdese que todas las sentencias if e if...else, equivalen a una nica sentencia por la posicin
que ocupan en el programa.
EJEMPLO N 5
Dado tres nmeros enteros determinar cul de ellos es el nmero mayor.
#include <iostream>
using namespace std;
int main()
{
int num1,num2,num3;
cout<<"Primer valor"<<endl;
cin>>num1;
cout<<"Segundo valor"<<endl;
cin>>num2;
cout<<"Tercer valor"<<endl;
cin>>num3;
if (num1>num2)
{
if(num1>num3)
cout<<"El mayor es: "<<num1;
else
cout<<"El mayor es: "<<num3;
}
else
31
HARDWARE - SOFTWARE
{
if (num2>num3)
cout<<"El mayor es: "<<num2;
else
cout<<"El mayor es: "<<num3;
}
return 0;
}
4.2 BUCLES
Adems de bifurcaciones, en el lenguaje C++ existen tambin varias sentencias que permiten
repetir una serie de veces la ejecucin de unas lneas de cdigo. Esta repeticin se realiza, bien un
nmero determinado de veces, bien hasta que se cumpla una determinada condicin de tipo
lgico o aritmtico. De modo genrico, a estas sentencias se les denomina bucles. Las tres
construcciones del lenguaje C para realizar bucles son el while, el for y el do...while.
4.2.1 SENTENCIA WHILE
Esta sentencia permite ejecutar repetidamente, mientras se cumpla una determinada condicin,
una sentencia o bloque de sentencias. La forma general es como sigue:
while (expresion_de_control)
sentencia;
Explicacin: Se evala expresion_de_control y si el resultado es false se salta sentencia y se
prosigue la ejecucin. Si el resultado es true se ejecuta sentencia y se vuelve a evaluar
expresion_de_control (evidentemente alguna variable de las que intervienen en
expresion_de_control habr tenido que ser modificada, pues si no el bucle continuara
indefinidamente). La ejecucin de sentencia prosigue hasta que expresion_de_control se hace
false, en cuyo caso la ejecucin contina en la lnea siguiente a sentencia. En otras palabras,
sentencia se ejecuta repetidamente mientras expresion_de_control sea true, y se deja de
ejecutar cuando expresion_de_control se hace false. Obsrvese que en este caso el control para
decidir si se sale o no del bucle est antes de sentencia, por lo que es posible que sentencia no se
llegue a ejecutar ni una sola vez. Todas las sentencias pueden ser simples o compuestas. (bloques
{ ... }).
EJEMPLO N 6
Realizar un programa que suma dos nmeros hasta que los tales sean 0. El programa termina
cuando los datos son 0.
#include<iostream>
using namespace std;
int main()
{
int a, b;
while (cin>>a>>b && a>0 && b>0)
{
cout<<a+b<<endl;
32
HARDWARE - SOFTWARE
}
return 0;
}
4.2.2 SENTENCIA FOR
For es quizs el tipo de bucle ms verstil y utilizado del lenguaje C++. Su forma general es la
siguiente:
for (inicializacin; expresion_de_control; actualizacin)
sentencia;
Explicacin: Posiblemente la forma ms sencilla de explicar la sentencia for sea utilizando la
construccin while que sera equivalente. Dicha construccin es la siguiente:
inicializacion;
while (expresion_de_control)
{
sentencia;
actualizacion;
}
Donde sentencia puede ser una nica sentencia terminada con (;), otra sentencia de control
ocupando varias lneas (if, while, for, ...), o una sentencia compuesta o un bloque encerrado entre
llaves {...}. Antes de iniciarse el bucle se ejecuta inicializacin, que es una o ms sentencias que
asignan valores iniciales a ciertas variables o contadores. A continuacin se evala
expresion_de_control y si es false se prosigue en la sentencia siguiente a la construccin for; si es
true se ejecutan sentencia y actualizacin, y se vuelve a evaluar expresion_de_control. El
proceso prosigue hasta que expresion_de_control sea false. La parte de actualizacion sirve para
actualizar variables o incrementar contadores. Un ejemplo tpico puede ser el producto escalar de
dos vectores a y b de dimensin n:
for (pe =0.0, i=1; i<=n; i++)
{
pe += a[i]*b[i];
}
Primeramente se inicializa la variable pe a cero y la variable i a 1; el ciclo se repetir mientras que
i sea menor o igual que n, y al final de cada ciclo el valor de i se incrementar en una unidad. En
total, el bucle se repetir n veces. La ventaja de la construccin for sobre la construccin while
equivalente est en que en la cabecera de la construccin for se tiene toda la informacin sobre
cmo se inicializan, controlan y actualizan las variables del bucle.
Obsrvese que la inicializacin consta de dos sentencias separadas por el operador (,).
EJEMPLO N 7
Disear un programa para generar la tabla de multiplicar de cualquier nmero introducido por
teclado.
33
HARDWARE - SOFTWARE
#include <iostream>
using namespace std;
int main()
{
int num;
cout<<"Inserte el numero que desa generar la tabla"<<endl;
cin>>num;
for (int i=1;i<=10;i++)
{
cout<<num<<" * "<<i<<" = "<<num*i<<endl;
}
return 0;
}
Donde sentencia puede ser una nica sentencia o un bloque, y en la que debe observarse que
hay que poner (;) a continuacin del parntesis que encierra a expresion_de_control, entre otros
motivos para que esa lnea se distinga de una sentencia while ordinaria.
EJEMPLO N 8
Realizar un programa que permita determinar la suma de la siguiente seria numrica:
S=2+4+6+8+10+N;
#include <iostream>
using namespace std;
int main()
{
int n,s=0,t=2,a=1;
cout<<"Ultimo termino a generar: "<<endl;
cin>>n;
cout<<endl;
do
{
cout<<t<<'\t';
s+=t;
t+=2;
34
HARDWARE - SOFTWARE
if(t-2==n)a=0;
}while (a);
cout<<endl<<endl<<"La suma es: "<<s;
return 0;
}
4.3 GENERACIN DE NMEROS ALEATORIOS
En C++, existe una funcin llamada rand(), que genera nmeros aleatorios. El problema que
tiene esta funcin es que siempre que reinicies el programa, aparecern los mismos nmeros.
Para evitar esto, hay que darle un nmero semilla, el cual operar como base para la
generacin de la secuencia de nmeros. El problema con esto, es que si le damos un nmero
fijo, volvemos al problema anterior, ya que siempre utilizar la misma base definida y por
ende la secuencia ser la misma.
Entonces, lo que necesitamos es darle un nmero semilla dinmico, esto es, que vaya
cambiando cada vez que ejecutemos el programa.
Sabiendo esto, la funcin que da la semilla a rand() es srand(), que recibe como parmetro (lo
que va entre los parntesis) el nmero semilla, que en este caso, ser la hora del sistema en
segundos. As, a menos que el programa se ejecute 2 o ms veces en menos de un segundo,
los nmeros cambiarn.
La funcin para saber la hora actual del sistema es time(NULL).
Sabiendo esto, vamos al cdigo. Haremos un generador de nmeros aleatorios, donde la
cantidad de estos, la decidir el usuario, ingresando esta cantidad por teclado.
As que lo primero que tenemos que hacer es incluir la librera:
#include<cstdlib>
Luego inicializar los nmeros aleatorios incluyendo esto:
srand(time(NULL));
Solo hay que utilizar adems la librera time.h:
#include<time.h>
Luego guardar el nmero aleatorio en alguna parte:
num=rand();
Eso es bsicamente. Para ajustar el rango de nmero aleatorios podemos hacer varias cosas.
Nmero aleatorios entre 0 y 50:
num=rand()%51;
Nmero aleatorios entre 1 y 100:
num=1+rand()%(101-1);
35
HARDWARE - SOFTWARE
36
HARDWARE - SOFTWARE
PROBLEMAS PROPUESTOS
Con los fundamentos necesarios de C++ que hemos aprendido, ya ests listo para resolver los
siguientes problemas.
1.
2.
3.
4.
5.
37
HARDWARE - SOFTWARE
38
HARDWARE - SOFTWARE
Estndar
entero (integer)
real (real)
carcter (char)
lgico (boolean)
Definido por el programador subrango (subrange)
(No estndar)
enumerativo (enumerated)
Datos Estructurados
Estticos
Dinmicos
39
HARDWARE - SOFTWARE
En este caso estamos asignndole valores a los primeros 3 elementos del vector nombre. Notar
que largo debe ser mayor o igual a la cantidad de valores que le estamos asignando al vector, en
el caso de ser la misma cantidad no aporta informacin, por lo que el lenguaje nos permite
escribir:
tipo_elemento nombre[]={valor_0, valor_1, valor_2};
Que declarar nombre como el vector de largo 3.
Para acceder a un elemento accederemos a travs de su posicin.
Es decir:
tipo_elemento elemento[largo];
...
elemento[2] = valor_2;
Asumiendo
que
tenemos
guardando valor_2 en elemento[2].
el
vector
anterior
definido
estaramos
Las operaciones que se pueden realizar con vectores durante el proceso de resolucin de un
problema son:
Asignacin
Lectura / Escritura
Recorrido (Acceso secuencial)
Actualizar (Aadir, borrar, insertar)
Ordenacin
Busqueda.
40
HARDWARE - SOFTWARE
<iostream>
<cstdio>
<cstdlib>
<conio.h>
<vector>
<algorithm>
<string.h>
<time.h>
41
HARDWARE - SOFTWARE
v[i]=1+rand()%(al-1);
}
cout<<endl;
mostrar();
}
void modificar_posicion()
{
int c,d;
if (v[0]==-1)
{
cout<<"Vector no creado"<<endl;
}
else
{
cout<<"Que posicion desea modificar???"<<endl;
cin>>c;
if (c>n)
{
cout<<"Posicion Inexistente"<<endl;
cout<<"Introduzca la posicion del vector de "<<n<<" posiones"<<endl;
cin>>c;
}
cout<<"Introduzca el nuevo valor de la posicion"<<endl;
cin>>d;
}
cout<<"Vector Anterior"<<endl;
mostrar();
v[c-1]=d;
cout<<"Vector Modificado"<<endl;
mostrar();
}
void modificar_global()
{
for(int i=0;i<n;i++)
{
cout<<"Ingrese valores nuevos para la posicion "<<i+1<<endl;
cin>>v[i];
}
cout<<endl;
mostrar();
}
void eliminar_posicion()
{
int e;
if (v[0]==-1)
{
cout<<"Vector no creado"<<endl;
}
else
{
cout<<"Inserte posicion del vector que desea eliminar"<<endl;
cin>>e;
if (e>n)
{
cout<<"Posicion Inexistente"<<endl;
cout<<"Introduzca la posicion del vector de "<<n<<" posiones"<<endl;
cin>>e;
}
}
v[e-1]=0;
cout<<endl;
mostrar();
}
void eliminar_global()
{
if (v[0]==-1)
{
cout<<"No existe vector para eliminar, debe crear Vector"<<'\n'<<endl;
system("pause");
system("cls");
}
else
{
cout<<"Usted Elimino Todo el Vector debera volver a crear otro"<<endl<<'\n';
system("pause");
system("cls");
memset(v,-1,sizeof(v));
}
}
void ordenar_ascendente()
{
cout<<"Vector Anterior"<<endl;
for(int i=0;i<n;i++)
{
cout<<v[i]<<'\t';
}
cout<<endl<<'\n';
cout<<"Vector Ordenado Ascendentemente"<<endl;
for (int i=0;i<6;i++)
{
42
HARDWARE - SOFTWARE
sort(v,v+n);
}
mostrar();
}
void ordenar_descendente()
{
cout<<"Vector Anterior"<<endl;
for(int i=0;i<n;i++)
{
cout<<v[i]<<'\t';
}
cout<<endl<<'\n';
cout<<"Vector Ordenado Descendentemente"<<endl;
for (int i=6;i>=0;i--)
{
sort(v,v+n,greater<int>());
cout<<endl;
}
mostrar();
}
int main()
{
int op;
memset(v,-1,sizeof(v));
while (op!=8)
{
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
MENU VECTORES
"<<endl;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
1.- CREAR
"<<endl;
cout<<'\t'<<"
2.- INSERTAR
"<<endl;
cout<<'\t'<<"
3.- MODIFICAR
"<<endl;
cout<<'\t'<<"
4.- ELIMINAR
"<<endl;
cout<<'\t'<<"
5.- ORDENAR
"<<endl;
cout<<'\t'<<"
6.- MOSTRAR
"<<endl;
cout<<'\t'<<"
7.- SALIR
"<<endl;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
OPCION: ";cin>>op;
system("cls");
if (op!=8)
{
switch(op)
{
case 1: crear(); break;
case 2: int b;
if (v[0]==-1)
{
cout<<"El vector no existe"<<endl;
system("pause");
system("cls");
}
else
if (v[0]>0)
{
cout<<"Vetor Lleno"<<endl;
system("pause");
system("cls");
}
else
{
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
MENU VECTORES - INSERTAR
"<<endl;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
1.- POR POSICION
"<<endl;
cout<<'\t'<<"
2.- GLOBAL
"<<endl;
cout<<'\t'<<"
3.- VOLVER
"<<endl;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
OPCION: ";cin>>b;
system("cls");
switch(b)
{
case 1: insertar_posicion(); break;
case 2: int h;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
MENU VECTORES - GLOBAL
"<<endl;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
1.- INSERTAR MANUALMENTE
"<<endl;
cout<<'\t'<<"
2.- INSERTAR ALEATORIAMENTE
"<<endl;
cout<<'\t'<<"
3.- VOLVER
"<<endl;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
OPCION: ";cin>>h;
system("cls");
switch(h)
{
case 1: insertar_manual(); break;
case 2: insertar_aleatorio(); break;
case 3: break;
}
break;
case 3: break;
}
}
break;
43
HARDWARE - SOFTWARE
case 3: int q;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
MENU VECTORES - MODIFICAR
"<<endl;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
1.- POR POSICION
"<<endl;
cout<<'\t'<<"
2.- GLOBAL
"<<endl;
cout<<'\t'<<"
3.- VOLVER
"<<endl;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
OPCION: ";cin>>q;
system("cls");
switch(q)
{
case 1: modificar_posicion(); break;
case 2: modificar_global(); break;
case 3: break;
}
break;
case 4: int o;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
MENU VECTORES - ELIMINAR
"<<endl;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
1.- POR POSICION
"<<endl;
cout<<'\t'<<"
2.- GLOBAL
"<<endl;
cout<<'\t'<<"
3.- VOLVER
"<<endl;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
OPCION: ";cin>>o;
system("cls");
switch(o)
{
case 1: eliminar_posicion(); break;
case 2: eliminar_global(); break;
case 3: break;
}
break;
case 5: int l;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
MENU VECTORES - ORDENAR
"<<endl;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
1.- ASCENDENTE
"<<endl;
cout<<'\t'<<"
2.- DESCENDENTE
"<<endl;
cout<<'\t'<<"
3.- VOLVER
"<<endl;
cout<<'\t'<<"========================================="<<endl;
cout<<'\t'<<"
OPCION: ";cin>>l;
system("cls");
switch(l)
{
case 1: ordenar_ascendente(); break;
case 2: ordenar_descendente(); break;
case 3: break;
}
break;
case 6: mostrar(); break;
case 7: salir(); return 0;
}
}
}
return 0;
}
NOTA:
Para el diseo del men se utilizo la nueva librera de C++ llamada STL, la cual facilita al
programador el ahorro de memoria en el cdigo.
ALGUNAS DIFERENCIAS ENTRE LA LIBRERA STL Y LA CLASICA DE C++:
Mtodo de Ordenacin con la librera de C++ Clsica:
int aux;
for (int c2=0;c2<n;c2++
{
if(lista[c2]>lista[c2+1])
{
aux=lista[c2];
lista[c2]=lista[c2+1];
lista[c2+1]=aux;
}
}
44
HARDWARE - SOFTWARE
45
HARDWARE - SOFTWARE
La forma de acceder a los elementos de la matriz es utilizando su nombre e indicando los dos
subndices que van en los corchetes. Si Coloco int matriz[2][3]=10; estoy asignando al cuarto
elemento de la tercera fila el valor 10. No olvidar que tanto filas como columnas se enumeran a
partir de 0.
Ejemplo:
0,0
1,0
2,0
3,0
0,1
1,1
2,1
3,1
0,2
1,2
2,2
3,2
0,3
1,3
2,3
3,3
FILA
COLUMNA
5.5 PUNTEROS
No, no salgas corriendo todava. Aunque vamos a empezar con un tema que suele asustar a los
estudiantes de C++, no es algo tan terrible como se cuenta. Como se suele decir de los leones: no
son tan fieros como los pintan.
Los punteros proporcionan la mayor parte de la potencia al C++, y marcan la principal diferencia
con otros lenguajes de programacin.
Una buena comprensin y un buen dominio de los punteros pondr en tus manos una
herramienta de gran potencia. Un conocimiento mediocre o incompleto te impedir desarrollar
programas eficaces.
Por eso le dedicaremos mucha atencin y mucho espacio a los punteros. Es muy importante
comprender bien cmo funcionan y cmo se usan.
Creo que todos sabemos lo que es un puntero, fuera del mbito de la programacin. Usamos
punteros para sealar cosas sobre las que queremos llamar la atencin, como marcar puntos en
un mapa o detalles en una presentacin en pantalla. A menudo, usamos el dedo ndice para
sealar direcciones o lugares sobre los que estamos hablando o explicando algo. Cuando un dedo
no es suficiente, podemos usar punteros. Antiguamente esos punteros eran una vara de madera,
pero actualmente se usan punteros laser, aunque la idea es la misma. Un puntero tambin es el
smbolo que representa la posicin del ratn en una pantalla grfica. Estos punteros tambin se
usan para sealar objetos: enlaces, opciones de men, botones, etc. Un puntero sirve, pues, para
apuntar a los objetos a los que nos estamos refiriendo.
Pues en C++ un puntero es exactamente lo mismo. Probablemente habrs notado que a lo largo
del curso nos hemos referido a variables, constantes, etc como objetos. Esto ha sido intencionado
por el siguiente motivo:
C++ est diseado para la programacin orientada a objetos (POO), y en ese paradigma, todas las
entidades que podemos manejar son objetos.
Los punteros en C++ sirven para sealar objetos, y tambin para manipularlos.
46
HARDWARE - SOFTWARE
Ahora veamos cmo funcionan los punteros. Un puntero es un tipo especial de objeto que
contiene, ni ms ni menos que, la direccin de memoria de un objeto. Por supuesto, almacenada
a partir de esa direccin de memoria puede haber cualquier clase de objeto: un char, un int,
un float, un array, una estructura, una funcin u otro puntero. Seremos nosotros los responsables
de decidir ese contenido, al declarar el puntero.
De hecho, podramos decir que existen tantos tipos diferentes de punteros como tipos de objetos
puedan ser referenciados mediante punteros. Si tenemos esto en cuenta, los punteros que
apunten a tipos de objetos distintos, sern tipos diferentes. Por ejemplo, no podemos asignar a
un puntero a char el valor de un puntero a int.
Intentemos ver con mayor claridad el funcionamiento de los punteros. Podemos considerar la
memoria del ordenador como un gran array, de modo que podemos acceder a cada celda de
memoria a travs de un ndice. Podemos considerar que la primera posicin del array es la 0 celda
[0].
47
HARDWARE - SOFTWARE
48
HARDWARE - SOFTWARE
*pEntero;
Equivale a:
int*
pEntero;
49
HARDWARE - SOFTWARE
Otro detalle importante es que, aunque las tres formas de situar el asterisco en la declaracin
son equivalentes, algunas de ellas pueden inducirnos a error, sobre todo si se declaran varios
objetos en la misma lnea:
int* x, y;
En este caso, x es un puntero a int, pero y no es ms que un objeto de tipo int. Colocar el
asterisco junto al tipo puede que indique ms claramente que estamos declarando un
puntero, pero hay que tener en cuenta que slo afecta al primer objeto declarado, si
quisiramos declarar ambos objetos como punteros aint tendremos que hacerlo de otro
modo:
int* x,
// O:
int *x,
// O:
int* x;
int* y;
*y;
*y;
Segn este ejemplo, pA es un puntero a int que apunta a la direccin donde se almacena el
valor del entero A.
5.5.3. OBJETO APUNTADO POR UN PUNTERO
La operacin contraria es obtener el objeto referenciado por un puntero, con el fin de
manipularlo, ya sea modificando su valor u obteniendo el valor actual.
Para manipular el objeto apuntado por un puntero usaremos el operador de indireccin, que
es un asterisco (*).
En C++ es muy habitual que el mismo smbolo se use para varias cosas diferentes, este es el
caso del asterisco, que se usa como operador de multiplicacin, para la declaracin de
punteros y, como vemos ahora, como operador de indireccin.
50
HARDWARE - SOFTWARE
Como operador de indireccin slo est permitido usarlo con punteros, y podemos leerlo
como "objeto apuntado por".
Por ejemplo:
int *pEntero;
int x = 10;
int y;
pEntero = &y;
*pEntero = x; // (1)
En (1) asignamos al objeto apuntado por pEntero en valor del objeto x. Como pEntero apunta
al objeto y, esta sentencia equivale (segn la secuencia del programa), a asignar a y el valor
de x.
5.5.4. DIFERENCIA ENTRE PUNTEROS Y OTROS OBJETOS
Debemos tener muy claro, en el ejemplo anterior, que pEntero es un objeto del tipo "puntero
a int", pero que *pEntero NO es un objeto de tipo int, sino una expresin.
Por qu decimos esto?
Pues porque, como pasa con todos los objetos en C++, cuando se declaran slo se reserva
espacio para almacenarlos, pero no se asigna ningn valor inicial, (recuerda que nuestro
puntero para rboles no crea rbol cada vez que sealemos con l). El contenido del objeto
permanecer sin cambios, de modo que el valor inicial del puntero ser aleatorio e
indeterminado. Debemos suponer que contiene una direccin no vlida.
Si pEntero apunta a un objeto de tipo int, *pEntero ser el contenido de ese objeto, pero no
olvides que*pEntero es un operador aplicado a un objeto de tipo "puntero a int". Es
decir, *pEntero es una expresin, no un objeto.
Declarar un puntero no crear un objeto del tipo al que apunta. Por ejemplo: int *pEntero; no
crea un objeto de tipo int en memoria. Lo que crea es un objeto que puede contener la
direccin de memoria de un entero.
Podemos decir que existe fsicamente un objeto pEntero, y tambin que ese
objeto puede (aunque esto no es siempre cierto) contener la direccin de un objeto de
tipo int.
Como todos los objetos, los punteros tambin contienen "basura" cuando son declarados. Es
costumbre dar valores iniciales nulos a los punteros que no apuntan a ningn sitio concreto:
int *pEntero = 0; // Tambin podemos asignar el valor NULL
char *pCaracter = 0;
51
HARDWARE - SOFTWARE
NULL es una constante, que est definida como cero en varios ficheros de cabecera, como
"cstdio" o "iostream". Sin embargo, hay muchos textos que recomiendan usar el valor 0 para
asignar a punteros nulos, al menos en C++.
6. LISTAS ABIERTAS.
6.1. DEFINICIN
La forma ms simple de estructura dinmica es la lista abierta. En esta forma los nodos se
organizan de modo que cada uno apunta al siguiente, y el ltimo no apunta a nada, es decir, el
puntero del nodo siguiente vale NULL.
En las listas abiertas existe un nodo especial: el primero. Normalmente diremos que nuestra lista
es un puntero a ese primer nodo y llamaremos a ese nodo la cabeza de la lista. Eso es porque
mediante ese nico puntero podemos acceder a toda la lista.
Cuando el puntero que usamos para acceder a la lista vale NULL, diremos que la lista est vaca.
El nodo tpico para construir listas tiene esta forma:
struct nodo \{
int dato;
struct nodo *siguiente;
};
En el ejemplo, cada elemento de la lista slo contiene un dato de tipo entero, pero en la prctica
no hay lmite en cuanto a la complejidad de los datos a almacenar.
6.2. DECLARACIONES DE TIPOS PARA MANEJAR LISTAS EN C++
Normalmente se definen varios tipos que facilitan el manejo de las listas, en C, la declaracin de
tipos puede tener una forma parecida a esta:
typedef struct _nodo \{
int dato;
struct _nodo *siguiente;
} tipoNodo;
typedef tipoNodo *pNodo;
typedef tipoNodo *Lista;
Lista enlazada
52
HARDWARE - SOFTWARE
Es muy importante que nuestro programa nunca pierda el valor del puntero al primer elemento,
ya que si no existe ninguna copia de ese valor, y se pierde, ser imposible acceder al nodo y no
podremos liberar el espacio de memoria que ocupa.
6.3. OPERACIONES BSICAS CON LISTAS
Con las listas tendremos un pequeo repertorio de operaciones bsicas que se pueden realizar:
Aadir o insertar elementos.
Buscar o localizar elementos.
Borrar elementos.
Moverse a travs de una lista, anterior, siguiente, primero.
Cada una de estas operaciones tendr varios casos especiales, por ejemplo, no ser lo mismo
insertar un nodo en una lista vaca, o al principio de una lista no vaca, o la final, o en una posicin
intermedia.
6.4. INSERTAR ELEMENTOS EN UNA LISTA ABIERTA
Veremos primero los casos sencillos y finalmente construiremos un algoritmo genrico para la
insercin de elementos en una lista.
6.4.1. INSERTAR UN ELEMENTO EN UNA LISTA VACA
Este es, evidentemente, el caso ms sencillo. Partiremos de que ya tenemos el nodo a insertar
y, por supuesto un puntero que apunte a l, adems el puntero a la lista valdr NULL:
Lista vaca
Insertar al principio
Podemos considerar el caso anterior como un caso particular de ste, la nica diferencia es
que en el caso anterior la lista es una lista vaca, pero siempre podemos, y debemos
considerar una lista vaca como una lista.
De nuevo partiremos de un nodo a insertar, con un puntero que apunte a l, y de una lista, en
este caso no vaca:
53
HARDWARE - SOFTWARE
Insertado al principio
Insertar al final
Insertado al final
Insertar dentro
Suponemos que ya disponemos del nuevo nodo a insertar, apuntado por nodo, y un puntero
al nodo a continuacin del que lo insertaremos.
54
HARDWARE - SOFTWARE
Insertado dentro
Supongamos que slo queremos mostrar los valores hasta que encontremos uno que sea mayor
que 100, podemos sustituir el bucle por:
...
indice = Lista;
while(indice && indice->dato <= 100) \{
printf("%d\n", indice->dato);
indice = indice->siguiente;
55
HARDWARE - SOFTWARE
}
...
Si analizamos la condicin del bucle, tal vez encontremos un posible error: Qu pasara si ningn
valor es mayor que 100, y alcancemos el final de la lista?. Podra pensarse que cuando indice sea
NULL, si intentamos acceder a indice->dato se producir un error.
En general eso ser cierto, no puede accederse a punteros nulos. Pero en este caso, ese acceso
est dentro de una condicin y forma parte de una expresin "and". Recordemos que cuando se
evala una expresin "and", se comienza por la izquierda, y la evaluacin se abandona cuando una
de las expresiones resulta falsa, de modo que la expresin "indice->dato <= 100" nunca se
evaluar si indice es NULL.
Si hubiramos escrito la condicin al revs, el programa nunca funcionara bien. Esto es algo muy
importante cuando se trabaja con punteros.
6.6. ELIMINAR ELEMENTOS EN UNA LISTA ABIERTA
De nuevo podemos encontrarnos con varios casos, segn la posicin del nodo a eliminar.
6.6.1. ELIMINAR EL PRIMER NODO DE UNA LISTA ABIERTA
Es el caso ms simple. Partiremos de una lista con uno o ms nodos, y usaremos un puntero
auxiliar, nodo:
1. Hacemos que nodo apunte al primer elemento de la lista, es decir a Lista.
2. Asignamos a Lista la direccin del segundo nodo de la lista: Lista->siguiente.
3. Liberamos la memoria asignada al primer nodo, el que queremos eliminar.
Si no guardamos el puntero al primer nodo antes de actualizar Lista, despus nos resultara
imposible liberar la memoria que ocupa. Si liberamos la memoria antes de actualizar Lista,
perderemos el puntero al segundo nodo.
Si la lista slo tiene un nodo, el proceso es tambin vlido, ya que el valor de Lista->siguiente
es NULL, y despus de eliminar el primer nodo la lista quedar vaca, y el valor de Lista ser
NULL.
De hecho, el proceso que se suele usar para borrar listas completas es eliminar el primer
nodo hasta que la lista est vaca.
56
HARDWARE - SOFTWARE
Eliminar un nodo
Nodo eliminado
57
HARDWARE - SOFTWARE
58
HARDWARE - SOFTWARE
nuevo->siguiente = anterior->siguiente;
anterior->siguiente = nuevo;
}
}
59
HARDWARE - SOFTWARE
anterior->siguiente = nodo->siguiente;
free(nodo);
}
}
20);
10);
40);
30);
MostrarLista(lista);
Borrar(&lista,
Borrar(&lista,
Borrar(&lista,
Borrar(&lista,
Borrar(&lista,
10);
15);
45);
30);
40);
MostrarLista(lista);
BorrarLista(&lista);
getchar();
return 0;
}
void Insertar(Lista *lista, int v) \{
60
HARDWARE - SOFTWARE
61
HARDWARE - SOFTWARE
}
}
void MostrarLista(Lista lista) \{
pNodo nodo = lista;
if(ListaVacia(lista)) printf("Lista vaca\n");
else \{
while(nodo) \{
printf("%d -> ", nodo->valor);
nodo = nodo->siguiente;
}
printf("\n");
}
}
62
HARDWARE - SOFTWARE
pnodo primero;
pnodo actual;
};
Hemos hecho que la clase para lista sea algo ms completa que la equivalente en C, aprovechando
las prestaciones de las clases. En concreto, hemos aadido funciones para mantener un puntero a
un elemento de la lista y para poder moverse a travs de ella.
Los algoritmos para insertar y borrar elementos son los mismos que expusimos para el ejemplo C,
tan slo cambia el modo de crear y destruir nodos.
6.11. CDIGO DEL EJEMPLO COMPLETO
#include <iostream>
using namespace std;
class nodo \{
public:
nodo(int v, nodo *sig = NULL)
\{
valor = v;
siguiente = sig;
}
private:
int valor;
nodo *siguiente;
friend class lista;
};
typedef nodo *pnodo;
class lista \{
public:
lista() \; }
~lista();
void Insertar(int v);
void Borrar(int v);
bool ListaVacia() \; }
void Mostrar();
void Siguiente() \
void Primero() \
void Ultimo() \
bool Actual() \; }
int ValorActual() \
private:
pnodo primero;
pnodo actual;
};
lista::~lista() \{
63
HARDWARE - SOFTWARE
pnodo aux;
while(primero) \{
aux = primero;
primero = primero->siguiente;
delete aux;
}
actual = NULL;
}
void lista::Insertar(int v) \{
pnodo anterior;
// Si la lista est vaca
if(ListaVacia() || primero->valor > v) \{
// Asignamos a lista un nuevo nodo de valor v y
// cuyo siguiente elemento es la lista actual
primero = new nodo(v, primero);
} else \{
// Buscar el nodo de valor menor a v
anterior = primero;
// Avanzamos hasta el ltimo elemento o hasta que el siguiente tenga
// un valor mayor que v
while(anterior->siguiente && anterior->siguiente->valor <= v)
anterior = anterior->siguiente;
// Creamos un nuevo nodo despus del nodo anterior, y cuyo siguiente
// es el siguiente del anterior
anterior->siguiente = new nodo(v, anterior->siguiente);
}
}
void lista::Borrar(int v) \{
pnodo anterior, nodo;
nodo = primero;
anterior = NULL;
while(nodo && nodo->valor < v) \{
anterior = nodo;
nodo = nodo->siguiente;
}
if(!nodo || nodo->valor != v) return;
else \{ // Borrar el nodo
if(!anterior) // Primer elemento
primero = nodo->siguiente;
else // un elemento cualquiera
anterior->siguiente = nodo->siguiente;
delete nodo;
}
}
void lista::Mostrar() \{
nodo *aux;
aux = primero;
while(aux) \{
64
HARDWARE - SOFTWARE
7. PILAS
7.1. DEFINICIN
Una pila es un tipo especial de lista abierta en la que slo se pueden insertar y eliminar nodos en
uno de los extremos de la lista. Estas operaciones se conocen como "push" y "pop",
respectivamente "empujar" y "tirar". Adems, las escrituras de datos siempre son inserciones de
nodos, y las lecturas siempre eliminan el nodo ledo.
Estas caractersticas implican un comportamiento de lista LIFO (Last In First Out), el ltimo en
entrar es el primero en salir.
El smil del que deriva el nombre de la estructura es una pila de platos. Slo es posible aadir
platos en la parte superior de la pila, y slo pueden tomarse del mismo extremo.
El nodo tpico para construir pilas es el mismo que vimos en el captulo anterior para la
construccin de listas:
struct nodo \{
int dato;
65
HARDWARE - SOFTWARE
Pila
Es evidente, a la vista del grfico, que una pila 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.
Teniendo en cuenta que las inserciones y borrados en una pila se hacen siempre en un extremo,
lo que consideramos como el primer elemento de la lista es en realidad el ltimo elemento de la
pila.
7.3. OPERACIONES BSICAS CON PILAS
Las pilas tienen un conjunto de operaciones muy limitado, slo permiten las operaciones de
"push" y "pop":
Push: Aadir un elemento al final de la pila.
Pop: Leer y eliminar un elemento del final de la pila.
7.4. PUSH, INSERTAR ELEMENTO
Las operaciones con pilas son muy simples, no hay casos especiales, salvo que la pila est vaca.
7.4.1. PUSH EN UNA PILA VACA
Partiremos de que ya tenemos el nodo a insertar y, por supuesto un puntero que apunte a l,
adems el puntero a la pila valdr NULL:
66
HARDWARE - SOFTWARE
Push en vaca
Podemos considerar el caso anterior como un caso particular de ste, la nica diferencia es que
podemos y debemos trabajar con una pila vaca como con una pila normal.
De nuevo partiremos de un nodo a insertar, con un puntero que apunte a l, y de una pila, en
este caso no vaca:
Resultado
Pop
Ahora slo existe un caso posible, ya que slo podemos leer desde un extremo de la pila.
Partiremos de una pila con uno o ms nodos, y usaremos un puntero auxiliar, nodo:
Resultado
67
HARDWARE - SOFTWARE
class nodo \{
public:
nodo(int v, nodo *sig = NULL) \{
valor = v;
siguiente = sig;
}
private:
int valor;
nodo *siguiente;
friend class pila;
};
typedef nodo *pnodo;
class pila \{
public:
pila() : ultimo(NULL) \{}
~pila();
void Push(int v);
int Pop();
private:
pnodo ultimo;
};
68
HARDWARE - SOFTWARE
69
HARDWARE - SOFTWARE
ultimo = nodo->siguiente;
/* Guardamos el valor de retorno */
v = nodo->valor;
/* Borrar el nodo */
delete nodo;
return v;
}
int main() \{
pila Pila;
Pila.Push(20);
cout << "Push(20)"
Pila.Push(10);
cout << "Push(10)"
cout << "Pop() = "
Pila.Push(40);
cout << "Push(40)"
Pila.Push(30);
cout << "Push(30)"
cout << "Pop() = "
cout << "Pop() = "
Pila.Push(90);
cout << "Push(90)"
cout << "Pop() = "
cout << "Pop() = "
<< endl;
<< endl;
<< Pila.Pop() << endl;
<< endl;
<< endl;
<< Pila.Pop() << endl;
<< Pila.Pop() << endl;
<< endl;
<< Pila.Pop() << endl;
<< Pila.Pop() << endl;
cin.get();
return 0;
}
template<class TIPO>
class nodo \{
public:
nodo(TIPO v, nodo<TIPO> *sig = NULL) \{
valor = v;
siguiente = sig;
}
private:
TIPO valor;
nodo<TIPO> *siguiente;
70
HARDWARE - SOFTWARE
71
HARDWARE - SOFTWARE
Eso es todo, ya slo falta usar nuestras clases para un ejemplo prctico:
#include <iostream>
#include "CCadena.h"
using namespace std;
template<class TIPO> class pila;
template<class TIPO>
class nodo \{
public:
nodo(TIPO v, nodo<TIPO> *sig = NULL) \{
valor = v;
siguiente = sig;
}
private:
TIPO valor;
nodo<TIPO> *siguiente;
friend class pila<TIPO>;
};
template<class TIPO>
class pila \{
public:
pila() : ultimo(NULL) \{}
~pila();
void Push(TIPO v);
TIPO Pop();
private:
nodo<TIPO> *ultimo;
};
template<class TIPO>
pila<TIPO>::~pila() \{
while(ultimo) Pop();
}
template<class TIPO>
void pila<TIPO>::Push(TIPO v) \{
nodo<TIPO> *nuevo;
/* Crear un nodo nuevo */
nuevo = new nodo<TIPO>(v, ultimo);
/* Ahora, el comienzo de nuestra pila es en nuevo nodo */
ultimo = nuevo;
}
template<class TIPO>
TIPO pila<TIPO>::Pop() \{
72
HARDWARE - SOFTWARE
<< ",";
<< ",";
<< ",";
<< endl;
<< ",";
<< ",";
<< ",";
<< endl;
73
HARDWARE - SOFTWARE
<< ",";
<< ",";
<< ",";
<< endl;
<< ",";
<< ",";
<< ",";
<< endl;
<< ",";
<< ",";
<< ",";
<< endl;
cin.get();
return 0;
}
8. COLAS
8.1. DEFINICIN
Una cola es un tipo especial de lista abierta en la que slo se pueden insertar nodos en uno de los
extremos de la lista y slo se pueden eliminar nodos en el otro. Adems, como sucede con las
pilas, las escrituras de datos siempre son inserciones de nodos, y las lecturas siempre eliminan el
nodo ledo.
Este tipo de lista es conocido como lista FIFO (First In First Out), el primero en entrar es el
primero en salir.
El smil cotidiano es una cola para comprar, por ejemplo, las entradas del cine. Los nuevos
compradores slo pueden colocarse al final de la cola, y slo el primero de la cola puede comprar
la entrada.
El nodo tpico para construir pilas es el mismo que vimos en los captulos anteriores para la
construccin de listas y pilas:
74
HARDWARE - SOFTWARE
struct nodo \{
int dato;
struct nodo *siguiente;
};
Cola
Es evidente, a la vista del grfico, 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. Adems, debido al funcionamiento de las colas, tambin
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 ms fcil ser insertar nodos por el final, a continuacin del nodo que no tiene nodo
siguiente, y leerlos desde el principio, hay que recordar que leer un nodo implica eliminarlo de la
cola.
8.3. OPERACIONES BSICAS CON COLAS
De nuevo nos encontramos ante una estructura con muy pocas operaciones disponibles. Las colas
slo permiten aadir y leer elementos:
Aadir: Inserta un elemento al final de la cola.
Leer: Lee y elimina un elemento del principio de la cola.
75
HARDWARE - SOFTWARE
Cola vaca
Partiremos de que ya tenemos el nodo a insertar y, por supuesto un puntero que apunte a l,
adems los punteros que definen la cola, primero y ltimo que valdrn NULL:
Elemento encolado
Cola no vaca
De nuevo partiremos de un nodo a insertar, con un puntero que apunte a l, y de una cola, en
este caso, al no estar vaca, los punteros primero y ltimo no sern nulos:
Elemento encolado
76
HARDWARE - SOFTWARE
Elemento desencolado
77
HARDWARE - SOFTWARE
Elemento desencolado
2. Asignamos NULL a primero, que es la direccin del segundo nodo terico de la cola: primero>siguiente.
3. Guardamos el contenido del nodo para devolverlo como retorno, recuerda que la operacin
de lectura en colas implican tambin borrar.
4. Liberamos la memoria asignada al primer nodo, el que queremos eliminar.
5. Hacemos que ultimo apunte a NULL, ya que la lectura ha dejado la cola vaca.
8.5.3. LEER UN ELEMENTO EN UNA COLA CASO GENERAL
1. Hacemos que nodo apunte al primer elemento de la pila, es decir a primero.
2. Asignamos a primero la direccin del segundo nodo de la pila: primero->siguiente.
3. Guardamos el contenido del nodo para devolverlo como retorno, recuerda que la operacin
de lectura en colas implican tambin borrar.
4. Liberamos la memoria asignada al primer nodo, el que queremos eliminar.
5. Si primero es NULL, hacemos que ultimo tambin apunte a NULL, ya que la lectura ha dejado
la cola vaca.
8.6 EJEMPLO DE COLA EN C++ USANDO CLASES
Ya hemos visto que las colas son casos particulares de listas abiertas, pero ms simples. Como en
los casos anteriores, veremos ahora un ejemplo de cola usando clases.
Para empezar, y como siempre, necesitaremos dos clases, una para nodo y otra para cola.
Adems la clase para nodo debe ser amiga de la clase cola, ya que sta debe acceder a los
miembros privados de nodo.
class nodo \{
public:
nodo(int v, nodo *sig = NULL) \{
78
HARDWARE - SOFTWARE
valor = v;
siguiente = sig;
}
private:
int valor;
nodo *siguiente;
friend class cola;
};
typedef nodo *pnodo;
class cola \{
public:
cola() : ultimo(NULL), primero(NULL) \{}
cola();
void Anadir(int v);
int Leer();
private:
pnodo primero, ultimo;
};
79
HARDWARE - SOFTWARE
int Pop();
private:
pnodo ultimo;
};
cola::~cola() \{
while(primero) Leer();
}
void cola::Anadir(int v) \{
pnodo nuevo;
/* Crear un nodo nuevo */
nuevo = new nodo(v);
/* Si la cola no estaba vaca, aadimos el nuevo a continuacin 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 vaca, ahora primero apuntar tambin al
nuevo nodo */
if(!primero) primero = nuevo;
}
int cola::Leer() \{
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 direccin del segundo nodo */
primero = nodo->siguiente;
/* Guardamos el valor de retorno */
v = nodo->valor;
/* Borrar el nodo */
delete nodo;
/* Si la cola qued vaca, ultimo debe ser NULL tambin*/
if(!primero) ultimo = NULL;
return v;
}
int main() \{
cola Cola;
Cola.Anadir(20);
cout << "Aadir(20)" << endl;
Cola.Anadir(10);
cout << "Aadir(10)" << endl;
cout << "Leer: " << Cola.Leer() << endl;
Cola.Anadir(40);
cout << "Aadir(40)" << endl;
Cola.Anadir(30);
cout << "Aadir(30)" << endl;
80
HARDWARE - SOFTWARE
<< endl;
<< endl;
<< endl;
<< endl;
cin.get();
return 0;
}
9. ARBOLES
9.1 DEFINICIN
Un rbol es una estructura no lineal en la que cada nodo puede apuntar a uno o varios nodos.
Tambin se suele dar una definicin recursiva: un rbol es una estructura en compuesta por un
dato y varios rboles.
Esto son definiciones simples. Pero las caractersticas que implican no lo son tanto.
rbol
81
HARDWARE - SOFTWARE
Otra caracterstica que normalmente tendrn nuestros rboles es que todos los nodos contengan
el mismo nmero de punteros, es decir, usaremos la misma estructura para todos los nodos del
rbol. Esto hace que la estructura sea ms sencilla, y por lo tanto tambin 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 raz de un rbol completo.
Existen otros conceptos que definen las caractersticas del rbol, en relacin a su tamao:
Orden: es el nmero 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 nmero de hijos que tiene el elemento con ms 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 ms de tres hijos.
Nivel: se define para cada elemento del rbol como la distancia a la raz, medida en nodos. El
nivel de la raz 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 raz de un rbol, tambin podemos
hablar de altura de ramas. El rbol del ejemplo tiene altura 3, la rama 'B' tiene altura 2, la
rama 'G' tiene altura 1, la 'H' cero, etc.
Los rboles de orden dos son bastante especiales, de hecho les dedicaremos varios captulos.
Estos rboles se conocen tambin como rboles binarios.
Frecuentemente, aunque tampoco es estrictamente necesario, para hacer ms fcil moverse a
travs del rbol, aadiremos un puntero a cada nodo que apunte al nodo padre. De este modo
podremos avanzar en direccin a la raz, y no slo hacia las hojas.
Es importante conservar siempre el nodo raz 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 tpico de un rbol difiere de los nodos que hemos visto hasta ahora para listas, aunque
slo en el nmero de nodos. Veamos un ejemplo de nodo para crear rboles de orden tres:
struct nodo \{
int dato;
struct nodo *rama1;
struct nodo *rama2;
struct nodo *rama3;
};
82
HARDWARE - SOFTWARE
O generalizando ms:
#define ORDEN 5
struct nodo \{
int dato;
struct nodo *rama[ORDEN];
};
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.
rbol es el tipo para declarar rboles de orden ORDEN.
rbol
El movimiento a travs de rboles, salvo que implementemos punteros al nodo padre, ser
siempre partiendo del nodo raz 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 algn 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 directorio y
nodos fichero, podramos considerar que los nodos hoja son ficheros y los nodos rama son
directorios.
Otro ejemplo podra ser la tabla de contenido de un libro, por ejemplo de este mismo curso,
dividido en captulos, y cada uno de ellos en subcaptulos. Aunque el libro sea algo lineal, como
83
HARDWARE - SOFTWARE
una lista, en el que cada captulo sigue al anterior, tambin es posible acceder a cualquier punto
de l a travs de la tabla de contenido.
Tambin se suelen organizar en forma de rbol los organigramas de mando en empresas o en el
ejrcito, y los rboles genealgicos.
9.3 OPERACIONES BSICAS CON RBOLES
Salvo que trabajemos con rboles especiales, como los que veremos ms adelante, las inserciones
sern siempre en punteros de nodos hoja o en punteros libres de nodos rama. Con estas
estructuras no es tan fcil generalizar, ya que existen muchas variedades de rboles.
De nuevo tenemos casi el mismo repertorio de operaciones de las que disponamos con las listas:
Aadir o insertar elementos.
Buscar o localizar elementos.
Borrar elementos.
Moverse a travs del rbol.
Recorrer el rbol completo.
Los algoritmos de insercin y borrado dependen en gran medida del tipo de rbol que estemos
implementando, de modo que por ahora los pasaremos por alto y nos centraremos ms en el
modo de recorrer rboles.
9.4 RECORRIDOS POR RBOLES
El modo evidente de moverse a travs de las ramas de un rbol es siguiendo los punteros, del
mismo modo en que nos movamos a travs de las listas.
Esos recorridos dependen en gran medida del tipo y propsito del rbol, pero hay ciertos
recorridos que usaremos frecuentemente. Se trata de aquellos recorridos que incluyen todo el
rbol.
Hay tres formas de recorrer un rbol completo, y las tres se suelen implementar mediante
recursividad. En los tres casos se sigue siempre a partir de cada nodo todas las ramas una por
una.
Supongamos que tenemos un rbol de orden tres, y queremos recorrerlo por completo.
Partiremos del nodo raz:
RecorrerArbol(raiz);
La funcin RecorrerArbol, aplicando recursividad, ser tan sencilla como invocar de nuevo a la
funcin RecorrerArbol para cada una de las ramas:
void RecorrerArbol(Arbol a) \{
if(a == NULL) return;
RecorrerArbol(a->rama[0]);
RecorrerArbol(a->rama[1]);
RecorrerArbol(a->rama[2]);
}
84
HARDWARE - SOFTWARE
Lo que diferencia los distintos mtodos de recorrer el rbol no es el sistema de hacerlo, sino el
momento que elegimos para procesar el valor de cada nodo con relacin a los recorridos de cada
una de las ramas.
rbol
85
HARDWARE - SOFTWARE
86
HARDWARE - SOFTWARE
87
HARDWARE - SOFTWARE
Buscar un elemento.
Insertar un elemento.
Borrar un elemento.
Movimientos a travs del rbol:
Izquierda.
Derecha.
Raz.
Informacin:
Comprobar si un rbol est vaco.
Calcular el nmero de nodos.
Comprobar si el nodo es hoja.
Calcular la altura de un nodo.
Calcular la altura de un rbol.
88
HARDWARE - SOFTWARE
89
HARDWARE - SOFTWARE
90
HARDWARE - SOFTWARE
91
HARDWARE - SOFTWARE
92
HARDWARE - SOFTWARE
Lo que haremos es buscar el elemento del nodo de que queremos averiguar la altura. Cada vez
que avancemos un nodo incrementamos la variable que contendr la altura del nodo.
Empezamos con el nodo raz apuntando a Raz, y la 'Altura' igual a cero.
Si el valor del nodo raz es igual que el del elemento que buscamos, terminamos la bsqueda
y el valor de la altura es 'Altura'.
Incrementamos 'Altura'.
Si el valor del nodo raz es mayor que el elemento que buscamos, continuaremos la bsqueda
en el rbol izquierdo.
Si el valor del nodo raz es menor que el elemento que buscamos, continuaremos la bsqueda
en el rbol derecho.
10.7.5. CALCULAR LA ALTURA DE UN RBOL.
La altura del rbol es la altura del nodo de mayor altura. Para buscar este valor tendremos que
recorrer todo el rbol, de nuevo es indiferente el tipo de recorrido que hagamos, cada vez que
cambiemos de nivel incrementamos la variable que contiene la altura del nodo actual, cuando
lleguemos a un nodo hoja compararemos su altura con la variable que contiene la altura del rbol
si es mayor, actualizamos la altura del rbol.
Iniciamos un recorrido del rbol en postorden, con la variable de altura igual a cero.
Cada vez que empecemos a recorrer una nueva rama, incrementamos la altura para ese
nodo.
Despus de procesar las dos ramas, verificamos si la altura del nodo es mayor que la variable
que almacena la altura actual del rbol, si es as, actualizamos esa variable.
10.8 RBOLES DEGENERADOS
Los rboles binarios de bsqueda tienen un gran inconveniente. Por ejemplo, supongamos que
creamos un ABB a partir de una lista de valores ordenada:
2, 4, 5, 8, 9, 12
Difcilmente podremos llamar a la estructura resultante un rbol:
93
HARDWARE - SOFTWARE
94
HARDWARE - SOFTWARE
De aqu se podra deducir que un grafo es bsicamente un objeto geomtrico aunque en realidad
sea un objeto combinatorio, es decir, un conjunto de puntos y un conjunto de lneas tomado de
entre el conjunto de lneas que une cada par de vrtices. Por otro lado, debido a su generalidad y
95
HARDWARE - SOFTWARE
a la gran diversidad de formas que pueden usarse, resulta complejo tratar con todas las ideas
relacionadas con un grafo.
Para facilitar el estudio de este tipo de dato, a continuacin se realizar un estudio de la teora de
grafos desde el punto de vista de las ciencias de la computacin, considerando que dicha teora
es compleja y amplia, aqu slo se realizar una introduccin a la misma, describindose el grafo
como un tipo de dato y mostrndose los problemas tpicos y los algoritmos que permiten
solucionarlos usando un ordenador.
Los grafos son estructuras de datos no lineales que tienen una naturaleza generalmente
dinmica, u estudio podra dividirse en dos grandes bloques; grafos dirigidos y grafos no dirigidos
(pueden ser considerados un caso particular de los anteriores).
Un ejemplo de grafo dirigido lo constituye la red de aguas de una ciudad ya que cada tubera slo
admite que el agua la recorra en un nico sentido, por el contrario, la red de carreteras de un pas
representa en general un grafo no dirigido, puesto que una misma carretera puede ser recorrida
en ambos sentidos. No obstante, podemos dar unas definiciones generales para ambos tipos.
A continuacin daremos definiciones de los dos tipos de grafos y de los conceptos que llevan
asociados.
11.2. NOTACIN Y CONCEPTOS
Un grafo G es un conjunto en el que hay definida una relacin binaria, es decir, G = (V,A) tal que V
es un conjunto de objetos a los que denominaremos vrtices o nodos y A V x V es una relacin
binaria a cuyos elementos denominaremos arcos o aristas.
Dados x, y V, puede ocurrir que:
(x, y)
(x, y)
Si las aristas tienen asociada una direccin (las aristas (x,y) y (y,x) no son equivalentes) diremos
que el grafo es dirigido, en otro caso ((x,y)=(y,x)) diremos que el grafo es no dirigido.
96
HARDWARE - SOFTWARE
Donde n=|V|.
Tanto a las aristas como a los vrtices les puede ser asociada informacin, a esta
informacin se le llama etiqueta, si la etiqueta que se asocia es un nmero se le llama
peso, costo o longitud, un grafo cuyas aristas o vrtices tienen pesos asociados recibe el
nombre de grafo etiquetado o ponderado.
El nmero de elementos de V se denomina orden del grafo, un grafo nulo es un grafo de
orden cero.
Se dice que un vrtice x es incidente a un vrtice y si existe un arco que vaya de x a y ((x,
y) A), a x se le denomina origen del arco y a y extremo del mismo, de igual forma
se dir que y es adyacente a x, en el caso de que el grafo sea no dirigido si x es adyacente
(resp. incidente) a y entonces y tambin es adyacente (resp. incidente) a x.
Se dice que dos arcos son adyacentes cuando tienen un vrtice comn que es a la vez
origen de uno y extremo del otro.
Se denomina camino (algunos autores lo llaman cadena si se trata de un grafo no
dirigido) en un grafo dirigido a una sucesin de arcos adyacentes:
97
HARDWARE - SOFTWARE
C= {(v1,v2),(v2,v3),...,(vn-1,vn), vi V}.
La longitud del camino es el nmero de arcos que comprende y en el caso en el que el
grafo sea ponderado se calcular como la suma de los pesos de las aristas que lo
constituyen.
Un camino se dice simple cuando todos sus arcos son distintos y se
dice elemental cuando no utiliza un mismo vrtice dos veces. Por tanto todo camino
elemental es simple y el recproco no es cierto.
Un camino se dice Euleriano si es simple y adems contiene a todos los arcos del grafo.
Un circuito (o ciclo para grafos no dirigidos) es un camino en el que coinciden los vrtices
inicial y final, un circuito se dice simple cuando todos los arcos que lo forman son
distintos y se dice elemental cuando todos los vrtices por los que pasa son distintos.
La longitud de un circuito es el nmero de arcos que lo componen. Un bucle es un
circuito de longitud 1 (estn permitidos los arcos de la forma (i, i) y notemos que un grafo
anti simtrico carecera de ellos).
Un circuito elemental que incluye a todos los vrtices de un grafo lo llamaremos
circuito Hamiltoniano.
Un grafo se denomina simple si no tiene bucles y no existe ms que un camino para unir
dos nodos.
Diremos que un grafo no dirigido es bipartido si el conjunto de sus vrtices puede ser
dividido en dos subconjuntos (disjuntos) de tal forma que cualquiera de las aristas que
componen el grafo tiene cada uno de sus extremos en un subconjunto distinto, un grafo
no dirigido ser bipartido si y slo si no contiene ciclos con un nmero de aristas par.
Dado un grafo G=(V,A),diremos que G'=(V,A' ) con A' A es un grafo parcial de G y
un subgrfo de G es todo grafo G'=(V',A') con V' V y A' A donde A' ser el conjunto
de todas aquellas aristas que unan en el grafo G dos vrtices que estn en V ', se podran
combinar ambas definiciones dando lugar a lo que llamaremos subgrfo parcial .
98
HARDWARE - SOFTWARE
e (x).
99
HARDWARE - SOFTWARE
Dado un grafo G, diremos que dos vrtices estn conectados si entre ambos existe un
camino que los une.
Llamaremos componente conexa a un conjunto de vrtices de un grafo tal que entre
cada par de vrtices hay al menos un camino y si se aade algn otro vrtice esta
condicin deja de verificarse. Matemticamente se puede ver como que la conexin es
una relacin de equivalencia que descompone a V en clases de equivalencia, cada uno de
los subgrfos a los que da lugar cada una de esas clases de equivalencia constituira una
componente conexa. Un grafo diremos que es conexo si slo existe una componente
conexa que coincide con todo el grafo.
100
HARDWARE - SOFTWARE
b. bien a travs de los vrtices de S (de los que se conoce su distancia ms corta al
origen),
Al final D contendr el costo del camino mnimo entre el origen y cada vrtice.
C es la matriz de costos del grafo. C [1,i] representa el costo del arco (1,i). Si el arco no
existe, se le asigna un valor fuera de rango ()
P es un vector de dimensin n a travs del cual reconstruiremos el camino ms corto del
origen al resto de los vrtices. As P[i] contiene el vrtice inmediato anterior a i en el
camino ms corto.
Inicialmente es evidente que S = {1} y i D[i] = C[1 i] con P[i] = 1
Con estas premisas el algoritmo de Dijkstra se puede esquematizar as:
Algoritmo Dijkstra ()
1. S = {1}
2. for (i = 2; i<=n; i++ )
{
D[i] = C[I,i]
P[i] = 1
}
3. while ( S V )
{
elegir w V-S / D[w] sea mnimo
S= S U{w}
for (cada vrtice v V -S)
if (D[v] > D[w] + C[w, v])
{
D[v] = D[w] + C[w, v]
P[v] = w
}
}
Veamos un ejemplo del algoritmo, supongamos que queremos encontrar los caminos mnimos
del vrtice 1 al resto en el grafo siguiente:
101
HARDWARE - SOFTWARE
En principio:
S= {1}
P[i] = 1 i
ITERACIN 1:
V -S = {2,3,4,5} w = 3 -> S = {1,3} -> V -S = {2,4,5}
D [2] = min(D [2] , D [3] + C [3,2]) = min(60, 50) = 50 -> P [2] = 3
D [4] = min(D [4] , D [3] + C [3,4]) = min(100, 40) = 40 -> P [4] = 3
D [5] = min(D [5] , D [3] + C [3,5]) = min (,) =
As D [2] = 50; D [4] = 40; D [5] = 00; P [2] = 3; P [4] = 3; P [5] = 1
ITERACIN 2:
V -S = {2,4,5} w = 4 -> S = {1,3,4} -> V- S = {2,5}
D [2] = min(D [2] , D [4] + C [4,2]) = min(50, 00) = 50 -> P [2] = 3
D [5] = min(D [5] , D [4] + C [4,5]) = min( 00,60) = 60 -> P [5] = 4
As D [2] = 50; D [5] = 60; p [2] = 3; P [5] = 4
102
HARDWARE - SOFTWARE
ITERACIN 3:
V -S = {2,5} w = 2 -> S = {1,3,4,2} -> V- S = {5}
D [5] = min(D [5] , D[2] + C [2,5]) = min(60, 55) = 55 -> P [5] = 2
As D [5] = 55; P[5] = 2
Finalmente w = 5 -> S = {1,3,4,2,5} -> FIN DEL ALGORITMO
Para reconstruir el camino ms corto del origen a cada vrtice, se asignan los predecesores en
orden inverso. Por ejemplo, si se quiere conocer el camino desde el origen al vrtice S, se, tiene
que:
P [5] = 2-+ P [2] = 3-+ P [3] = 1 siendo por tanto el camino (1,3,2,5) con costo 55 .
Aunque la implementacin de este algoritmo es simple si la realizamos en base a una matriz de
adyacencia, en la prctica se utiliza normalmente una implementacin en base a listas de
adyacencia. La razn de esta eleccin es que en la primera la eficiencia es O(n2) para cualquier
grafo; sin embargo la mayora de los grafos que nos encontramos en la prctica tiene un nmero
de aristas bastante pequeo (grafos que podemos denominar dispersos o no densos ) y por tanto
el uso de listas de adyacencia se presenta como una solucin ms eficiente. Para conseguir una
mejor eficiencia en esta implementacin del algoritmo de Dijkstra se ha echado mano de una
estructura de datos formada por un APO que tiene como etiqueta los vrtices del grafo y como
clave el coste de ir desde el vrtice inicial en el problema a ese vrtice de tal forma que obtener el
vrtice con mnimo coste sera O(log n).
11.3.2. ENTRE CADA PAR DE VRTICES DEL GRAFO
En lugar de buscar los caminos mnimos de un vrtice a los dems nos podemos plantear buscar
el camino ms corto entre cualquier pareja de vrtices, es decir, dado un grafo dirigido
etiquetado G = {V, A} en el que las etiquetas son no negativas encontrar el camino de longitud "
ms corta entre dos vrtices cualesquiera de ese grafo.
Podra pensarse, para resolver el problema, en aplicar el algoritmo de Dijkstra n veces, una por
vrtice, pero en lugar de eso, aplicaremos un nuevo algoritmo creado por Floyd que va
encontrando los caminos de forma iterativa.
NOTACIN:
103
HARDWARE - SOFTWARE
A es una matriz de tamao n x n en la que se calcular en cada Aij la longitud ms corta del
camino que va de i a j.
P es una matriz de tamao n x n que utilizaremos para recuperar los caminos ms cortos.
C es una matriz de dimensin n x n conteniendo los costos de los arcos. Si no existe arco de
un vrtice i a otro j el correspondiente valor C [i,j] = .
104
HARDWARE - SOFTWARE
Grafo ( ) -> constructor del grafo, reserva los recursos e inicializa el grafo a vaco.
Nodo LocalizaLabel (const Tbase e) -> localiza la etiqueta, devuelve el nodo asociado a la
etiqueta e.
PARMETROS: e -> etiqueta asociada al nodo a encontrar.
Bool ExisteArco (nodo o, nodo d) -> nos dice si existe un arco, devuelve true si existe el arco
o false en otro caso.
PARMETROS: o -> nodo origen del arco.
d -> nodo destino del arco.
105
HARDWARE - SOFTWARE
Int GrafoVacio () -> devuelve 0 si el grafo esta vacio, si no devuelve 1 en otro caso.
Float EtiqArco (nodo o, nodo d) -> devuelve la etiqueta asociada a un arco, es decir el peso
del arco.
PARMETROS: o -> nodo origen del arco.
d -> nodo destino del arco.
Void InsertarNodo (const Tbase dato) -> inserta un nodo nuevo en el grafo.
PARMETROS: dato -> etiqueta del nuevo nodo.
Void InsertarArco (nodo o, nodo d, int valor) -> inserta un arco entre el nodo o y el d,
asociado al arco le podemos dar un valor o peso.
PARMETROS: o -> nodo origen del arco.
d -> nodo destino del arco.
valor -> peso del del arco.
Void BorrarArco (nodo o, nodo d) -> borra el arco existente entre los nodos o y d.
PARMETROS: o -> nodo origen del arco.
d -> nodo destino del arco.
Void DesconectarNodo (nodo a_eliminar) -> elimina un nodo del grafo receptor, todos los
arcos que entran o salen del nodo a eliminar tambien desaparecen.
PARMETROS: a_eliminar -> nodo a eliminar del grafo.
Void CopiarGrafo (Grafo gr) -> copia el grafo gr en el receptor.
PARMETROS: gr -> grafo a copiar.
106
HARDWARE - SOFTWARE
{
n2 = a->destino;
printf (%3d -> %3d (%d), n1->etiqueta, n2->etiqueta, a->valor);
}
if (cont % 4)
printf (,);
else printf ( \n );
cont++;
}
}
if ((cont % 4) != 0)
printf ( \n );
}
107
HARDWARE - SOFTWARE
Si el grafo es etiquetado, entonces tanto bi,j como bi,j representan al coste o valor asociado al arco
(i,j) y se suelen denominar matrices de coste. Si el arco (i,j) no pertenece a A entonces se asigna
bi,j o bi,j un valor que no puede ser utilizado como una etiqueta valida.
La principal ventaja de la matriz de adyacencia es que el orden de eficiencia de las operaciones de
obtencin de etiqueta de un arco o ver si dos vrtices estn conectados son independientes del
nmero de vrtices y de arcos, por el contrario, existen dos grandes inconvenientes:
Es una representacin orientada hacia grafos que no modifica el nmero de sus vrtices ya
que una matriz no permite que se le o supriman filas o columnas.
Se puede producir un gran derroche de memoria en grafos poco densos (con gran nmero de
vrtices y escaso nmero de arcos).
Para evitar estos inconvenientes se introduce otra representacin: las listas de adyacencia.
11.6.2. LISTA DE ADYACENCIA
En esta estructura de datos la idea es asociar a cada vrtice i del grafo una lista que contenga
todos aquellos vrtices j que sean adyacentes a l. De esta forma slo reservar memoria para los
arcos adyacentes a i y no para todos los posibles arcos que pudieran tener como origen i, el grafo,
por tanto, se representa por medio de un vector de n componentes (si |V|=n) donde cada
componente va a ser una lista de adyacencia correspondiente a cada uno de los vrtices del
grafo. Cada elemento de la lista consta de un campo indicando el vrtice adyacente. En caso de
que el grafo sea etiquetado, habr que aadir un segundo campo para mostrar el valor de la
etiqueta.
108
HARDWARE - SOFTWARE
109
HARDWARE - SOFTWARE
Como puede verse en el ejemplo de las figuras anteriores tanto el vector de listas de adyacencias
como en la lista de listas se ha razonado en funcin de los vrtices que actan como orgenes de
los arcos. Anlogamente se poda haber hecho con los vrtices destino, y combinando ambas
representaciones podra pensarse en utilizar dos vectores de listas de adyacencia o dos listas de
listas de adyacencia.
Nuestra implementacin es con listas de adyacencia donde slo usamos los arcos adyacentes y no
los incidentes, es una simplificacin considerable ya que no tenemos mucho tiempo.
Usamos una estructura nodo que simula lo que sera un vrtice o nodo del grafo, contiene un
elemento de tipo Tbase que es la etiqueta, un entero nodo que identifica unvocamente al nodo,
un puntero sig al siguiente nodo de la lista y un puntero ady a la lista de adyacencia del nodo en
cuestin. Podra tener como hemos comentado antes un puntero a la lista de arcos incidentes
pero hemos simplificado el problema.
Tambin tenemos otra estructura para simular el comportamiento de un arco o arista del grafo,
esta estructura tiene un valor que es el peso que tiene el arco tratado, un puntero sig al siguiente
arco de la lista, un puntero al nodo origen del arco y otro al destino del mismo.
El grafo se compone de un nodo inicio que es el primer nodo del grafo, del cual cuelgan el resto
de los nodos y del nnodos que nos da el nmero de nodos del grafo.
Para el grafo de la siguiente figura:
110
HARDWARE - SOFTWARE
111
HARDWARE - SOFTWARE
}
struct arco
{
int valor;
struct nodo *origen;
struct nodo *destino;
struct arco *sig;
}
struct nodo *inicio;
int nnodos;
Grafo ();
nodo LocalizaLabel (const Tbase e);
bool ExisteArco (nodo o, nodo d);
int GrafoVacio ();
float EtiqArco (nodo o, nodo d);
void InsertarNodo (const Tbase dato);
void InsertarArco (nodo o, nodo d, int valor);
void BorrarArco (nodo o, nodo d);
void DesconectarNodo (nodo a_eliminar);
void CopiarGrafo (Grafo gr);
~Grafo();
}
Debemos aclarar un par de cosas sobre la implementacin en C++; slo tenemos miembros
pblicos para facilitar el acceso a los mismos de forma rpido, aunque esta no sea la filosofa ms
apropiada tratndose de un lenguaje con orientacin a objetos, esto se ha hecho as para
simplificar en gran medida tanto las primitivas como el cdigo asociado a ellas ya que en la
confeccin de este tutorial no hemos tenido tiempo suficiente para hacer una clase Grafo como
dios manda.
Si se quiere hacer una clase grafo donde tengamos miembros privados como inicio y nnodos,
debemos elaborar primitivas que nos devuelvan cada elemento de una estructura nodo y arco en
este caso, seran muchas primitivas pequeas, luego el resto (las que hemos elegido aqu) habra
que modificarlas en base a las nuevas primitivas elaboradas.
Pasemos ahora a ver la implementacin de cada una de las primitivas enunciadas con
anterioridad en C++:
CODIGO EN C++
template <class Tbase>
Grafo<Tbase>::Grafo()
{
inicio = new nodo;
112
HARDWARE - SOFTWARE
inicio->etiqueta = 0;
inicio->nodo = 1;
inicio->ady = 0;
inicio->sig = 0;
}
template <class Tbase>
nodo Grafo<Tbase>::LocalizaLabel(const Tbase e)
{
nodo n;
int enc=0;
for (n=this.inicio; n!=0 && !enc; )
{
if (n->etiqueta == e)
enc = 1;
else
n = n->sig;
}
return n;
}
template <class Tbase>
bool Grafo<Tbase>::ExisteArco(nodo o, nodo
{
arco a;
d)
a=o->ady;
while (a!=0)
{
if ((a->origen==o) && (a->destino==d))
return true;
else
a = a->sig;
}
return false;
}
template <class Tbase>
int Grafo<Tbase>::GrafoVacio()
{
return(this.inicio == 0);
}
template <class Tbase>
float Grafo<Tbase>::EtiqArco(nodo o, nodo d)
{
arco a;
a=o->ady;
while (a!=0)
{
if ((a->origen == o) && (a->destino == d))
return (a->valor);
else
a = a->sig;
113
HARDWARE - SOFTWARE
}
return 0;
}
template <class Tbase>
void Grafo<Tbase>::InsertarNodo(const Tbase dato)
{
nodo aux,p;
aux = new nodo;
p=this.inicio;
while(p->sig != 0)
p = p->sig;
aux->etiqueta = dato;
aux->nodo = (p->nodo)+1;
aux->ady = 0;
aux->sig = 0;
p->sig = aux;
(this.nnodos)++;
}
template <class Tbase>
void Grafo<Tbase>::InsertarArco (nodo , nodo d, int valor)
{
arco aux;
aux = new arco;
aux->origen = o;
aux->destino = d;
aux->valor = valor;
aux->sig= o->ady;
o->ady = aux;
}
template <class Tbase>
void Grafo<Tbase>::BorrarArco(nodo o, nodo d)
{
arco a,ant;
int enc=0;
if (o->ady==0)
return;
else
if (o->ady->destino==d)
{
a = o->ady;
o->ady = a->sig;
delete a;
}
else {
ant = o->ady;
a = ant->sig;
while (!enc && (a!=0))
{
if (a->destino==d)
114
HARDWARE - SOFTWARE
enc=1;
else {
a = a->sig;
ant = ant->sig;
}
}
if (a==0)
return;
else {
ant->sig = a->sig;
delete a;
}
}
}
template <class Tbase>
Void Grafo<Tbase>::DesconectarNodo(nodo a_eliminar)
{
Grafo g_nd;
nodo n,org,dst,o,d;
arco a;
g_nd = new Grafo;
for (n=this.inicio; n!=0; n=n->sig)
g_nd.InsertarNodo(n->etiqueta);
for (n=this.inicio; n!=0; n=n->sig)
for (a=n->ady; a!=0; a=a->sig)
{
org = a->origen;
dst = a->destino;
o = g_nd.LocalizaLabel(org->etiqueta);
d = g_nd.LocalizaLabel(dst->etiqueta);
if ((org!=a_eliminar) && dst!=a_eliminar))
g_nd.InsertarArco(o,d,a->valor);
}
this.CopiarGrafo(g_nd);
}
template <class Tbase>
Void Grafo<Tbase>::CopiarGrafo(Grafo gr)
{
nodo n,org,dest,o,d;
arco a;
this.Destruir();
this = new Grafo;
for (n=gr.inicio; n!=0; n=n->sig)
this.InsertarNodo(n->etiqueta);
for (n=gr.inicio; n!=0; n=n->sig)
for (a=n->ady; a!=0; a=a->sig)
{
org = a->origen;
dest = a->destino;
o = g_nd.LocalizaLabel(org->etiqueta);
d = g_nd.LocalizaLabel(dest->etiqueta);
115
HARDWARE - SOFTWARE
this.InsertarArco(o,d,a->valor);
}
}
template <class Tbase>
Grafo<Tbase>::~Grafo()
{
nodo n;
arco aux;
while ((this.inicio)->sig != 0)
{
n = (this.inicio)->sig;
while (n->ady != 0)
{
aux = n->ady;
n->ady = aux->sig;
delete aux;
}
(this.inicio)->sig = n->sig;
delete n;
}
delete (this.inicio);
}
116
HARDWARE - SOFTWARE
Un valor de retorno cero indica que el fichero ha sido correctamente cerrado, si ha habido
algn error, el valor de retorno es la constante EOF.
Un ejemplo pequeo para abrir y cerrar el archivo llamado fichero.in en modo lectura:
#include <iostream>
#include <cstdio>
using namespace std;
int main(int argc, char** argv)
{
FILE *fp;
fp = fopen ( "fichero.in", "r" );
fclose ( fp );
return 0;
}
117
HARDWARE - SOFTWARE
12.1.3 FEOF
Esta funcin sirve para determinar si el cursor dentro del archivo encontr el final (end of
file). Existe otra forma de verificar el final del archivo que es comparar el carcter que trae
fgetc del archivo con el macro EOF declarado dentro de <cstdio>, pero este mtodo no ofrece
la misma seguridad (en especial al tratar con los archivos "binarios"). La funcin feof siempre
devolver cero (Falso) si no es encontrado EOF en el archivo, de lo contrario regresar un
valor distinto de cero (Verdadero).
El prototipo correspondiente de feof es:
int feof(FILE *fichero);
12.1.4 REWIND
Literalmente significa "rebobinar", sita el cursor de lectura/escritura al principio del archivo.
El prototipo correspondiente de rewind es:
void rewind(FILE *fichero);
12.2 LECTURA
Un archivo generalmente debe verse como un string (una cadena de caracteres) que est
guardado en el disco duro. Para trabajar con los archivos existen diferentes formas y diferentes
funciones. Las funciones que podramos usar para leer un archivo son:
char fgetc(FILE *archivo)
char *fgets(char *buffer, int tamano, FILE *archivo)
size_t fread(void *puntero, size_t tamano, size_t cantidad, FILE *archivo);
int fscanf(FILE *fichero, const char *formato, argumento, ...);
Las primeras dos de estas funciones son muy parecidas entre s. Pero la tercera, por el nmero y
el tipo de parmetros, nos podemos dar cuenta de que es muy diferente, por eso la trataremos
aparte junto al fwrite que es su contraparte para escritura.
12.2.1 FGETC
Esta funcin lee un carcter a la vez del archivo que est siendo sealado con el puntero
*archivo. En caso de que la lectura sea exitosa devuelve el carcter ledo y en caso de que no
lo sea o de encontrar el final del archivo devuelve EOF.
El prototipo correspondiente de fgetc es:
char fgetc(FILE *archivo);
Esta funcin se usa generalmente para recorrer archivos de texto. A manera de ejemplo
vamos a suponer que tenemos un archivo de texto llamado "prueba.txt" en el mismo
directorio en que se encuentra la fuente de nuestro programa. Un pequeo programa que lea
ese archivo ser:
118
HARDWARE - SOFTWARE
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
int main()
{
FILE *archivo;
char caracter;
archivo = fopen("prueba.txt","r");
if (archivo == NULL){
printf("\nError de apertura del archivo. \n\n");
}else
{
12.2.2 FGETS
Esta funcin est diseada para leer cadenas de caracteres. Leer hasta n-1 caracteres o
hasta que lea un cambio de lnea '\n' o un final de archivo EOF. En este ltimo caso, el
carcter de cambio de lnea '\n' tambin es ledo.
El prototipo correspondiente de fgets es:
char *fgets(char *buffer, int tamao, FILE *archivo);
119
HARDWARE - SOFTWARE
int main()
{
FILE *archivo;
char caracteres[100];
archivo = fopen("prueba.txt","r");
if (archivo == NULL)
exit(1);
printf("\nEl contenido del archivo de prueba es \n\n");
while (feof(archivo) == 0)
{
fgets(caracteres,100,archivo);
printf("%s",caracteres);
}
system("PAUSE");
fclose(archivo);
return 0;
}
Este es el mismo ejemplo de antes con la diferencia de que este hace uso de fgets en lugar de
fgetc. La funcin fgets se comporta de la siguiente manera, leer del archivo apuntado por
archivo los caracteres que encuentre y a ponerlos en buffer hasta que lea un carcter menos
que la cantidad de caracteres especificada en tamao o hasta que encuentre el final de una
lnea (\n) o hasta que encuentre el final del archivo (EOF). En este ejemplo no vamos a
profundizar ms que para decir que caracteres es un buffer, los por menores sern explicados
en la seccin de manejo dinmico de memoria.
El beneficio de esta funcin es que se puede obtener una lnea completa a la vez.
12.2.3 FREAD
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
Esta funcin lee un bloque de una "stream" de datos. Efecta la lectura de un arreglo de
elementos "count", cada uno de los cuales tiene un tamao definido por "size". Luego los
guarda en el bloque de memoria especificado por "ptr". El indicador de posicin de la cadena
de caracteres avanza hasta leer la totalidad de bytes. Si esto es exitoso la cantidad de bytes
ledos es (size*count).
PARAMETROS:
ptr : Puntero a un bloque de memoria con un tamao mnimo de (size*count) bytes.
size : Tamao en bytes de cada elemento (de los que voy a leer).
count : Nmero de elementos, los cuales tienen un tamao "size".
stream: Puntero a objetos FILE, que especifica la cadena de entrada.
120
HARDWARE - SOFTWARE
12.2.4 FSCANF
La funcin fscanf funciona igual que scanf en cuanto a parmetros, pero la entrada se toma
de un fichero en lugar del teclado.
El prototipo correspondiente de fscanf es:
int fscanf(FILE *fichero, const char *formato, argumento, ...);
12.3 ESCRITURA
As como podemos leer datos desde un fichero, tambin se pueden crear y escribir ficheros con la
informacin que deseamos almacenar, Para trabajar con los archivos existen diferentes formas y
diferentes funciones. Las funciones que podramos usar para escribir dentro de un archivo son:
int fputc(int caracter, FILE *archivo)
int fputs(const char *buffer, FILE *archivo)
size_t fwrite(void *puntero, size_t tamano, size_t cantidad, FILE *archivo);
int fprintf(FILE *archivo, const char *formato, argumento, ...);
12.3.1 FPUTC
Esta funcin escribe un carcter a la vez del archivo que est siendo sealado con el puntero
*archivo. El valor de retorno es el carcter escrito, si la operacin fue completada con xito,
en caso contrario ser EOF.
El prototipo correspondiente de fputc es:
121
HARDWARE - SOFTWARE
Mostramos un ejemplo del uso de fputc en un "fichero.txt", se escribir dentro del fichero
hasta que presionemos la Tecla Enter.
#include <iostream>
#include <cstdio>
using namespace std;
int main ( int argc, char **argv )
{
FILE *fp;
char caracter;
fp = fopen ( "fichero.txt", "r+" );
printf("\nIntrouce un texto al fichero: ");
while((caracter = getchar()) != '\n')
{
printf("%c", fputc(caracter, fp));
}
fclose ( fp );
return 0;
}
12.3.2 FPUTS
La funcin fputs escribe una cadena en un fichero. No se aade el carcter de retorno de lnea
ni el carcter nulo final. El valor de retorno es un nmero no negativo o EOF en caso de error.
Los parmetros de entrada son la cadena a escribir y un puntero a la estructura FILE del
fichero donde se realizar la escritura.
El prototipo correspondiente de fputs es:
int fputs(const char *buffer, FILE *archivo)
122
HARDWARE - SOFTWARE
12.3.3 FWRITE
Esta funcin est pensada para trabajar con registros de longitud constante y forma pareja
con fread. Es capaz de escribir hacia un fichero uno o varios registros de la misma longitud
almacenados a partir de una direccin de memoria determinada. El valor de retorno es el
nmero de registros escritos, no el nmero de bytes. Los parmetros son: un puntero a la
zona de memoria de donde se obtendrn los datos a escribir, el tamao de cada registro, el
nmero de registros a escribir y un puntero a la estructura FILE del fichero al que se har la
escritura.
El prototipo correspondiente de fwrite es:
size_t fwrite(void *puntero, size_t tamano, size_t cantidad, FILE *archivo);
Un ejemplo concreto del uso de fwrite con su contraparte fread y usando funciones es:
/*
*
*
*
*
*
*
*/
FicheroCompleto.cpp
Copyright 2012 Alain Wilfredo Fuentes Garcia
Cel: 60413636
E-mail: lamente.maestra2014@hotmail.es>
#include <iostream>
#include <cstdio>
using namespace std;
void
void
void
void
menu();
CrearFichero(FILE *Fichero);
InsertarDatos(FILE *Fichero);
VerDatos(FILE *Fichero);
struct sRegistro {
char Nombre[25];
int Edad;
float Sueldo;
} registro;
int main()
{
int opcion;
int exit = 0;
FILE *fichero;
while (!exit)
{
123
HARDWARE - SOFTWARE
menu();
printf("\nOpcion: ");
scanf("%d", &opcion);
switch(opcion)
{
case 1:
CrearFichero(fichero);
break;
case 2:
InsertarDatos(fichero);
break;
case 3:
VerDatos(fichero);
break;
case 4:
exit = 1;
break;
default:
printf("\n opcion no valida");
}
}
return 0;
}
void menu()
{
printf("\nMenu:");
printf("\n\t1. Crear fichero");
printf("\n\t2. Insertar datos");
printf("\n\t3. Ver datos");
printf("\n\t4. Salir");
}
void CrearFichero(FILE *Fichero)
{
Fichero = fopen("fichero", "r");
if(!Fichero)
{
Fichero = fopen("fichero", "w");
printf("\nArchivo creado!");
}
else
{
printf("\nEl fichero ya existe!");
}
fclose (Fichero);
return;
}
void InsertarDatos(FILE *Fichero)
{
Fichero = fopen("fichero", "a+");
if(Fichero == NULL)
{
printf("\nFichero no existe! \nPor favor creelo");
return;
124
HARDWARE - SOFTWARE
}
printf("\nDigita el nombre: ");
scanf("%s", ®istro.Nombre);
printf("\nDigita la edad: ");
scanf("%d", ®istro.Edad);
printf("\nDigita el sueldo: ");
scanf("%f", ®istro.Sueldo);
fwrite(®istro, sizeof(struct sRegistro), 1, Fichero);
fclose(Fichero);
return;
}
void VerDatos(FILE *Fichero)
{
int numero = 1;
Fichero = fopen("fichero", "r");
if(Fichero == NULL)
{
printf("\nFichero no existe! \nPor favor creelo");
return;
}
fread(®istro, sizeof(struct sRegistro), 1, Fichero);
printf("\nNumero \tNombre \tEdad \tSueldo");
while(!feof(Fichero))
{
printf("\n%d \t%s \t%d \t%.2f", numero, registro.Nombre,
registro.Edad, registro.Sueldo);
fread(®istro, sizeof(struct sRegistro), 1, Fichero);
numero++;
}
fclose(Fichero);
return;
}
12.3.4 FPRINTF
La funcin fprintf funciona igual que printf en cuanto a parmetros, pero la salida se dirige a
un archivo en lugar de a la pantalla.
El prototipo correspondiente de fprintf es:
int fprintf(FILE *archivo, const char *formato, argumento, ...);
Podemos ver un ejemplo de su uso, abrimos el documento "fichero.txt" en modo
lectura/escritura y escribimos dentro de l.
#include <iostream>
#include <cstdio>
using namespace std;
125
HARDWARE - SOFTWARE
126
HARDWARE - SOFTWARE
127
HARDWARE - SOFTWARE
la
circunferencia
#include <iostream>
using namespace std;
int main()
{
int nota;
cout<<"LA NOTA MINIMA DE APROBACION ES DE 51"<<endl<<endl;
cout<<"Introduzca Nota: ";
cin>>nota;
cout<<endl;
if (nota>=51)
cout<<"Es una nota de aprobacion";
else
cout<<"Es una nota de reprobacion";
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int num;
cout<<"Itroduzca un numero: ";
128
es:
HARDWARE - SOFTWARE
cin>>num;
cout<<endl;
if (num % 2==0)
cout<<"El numero introducido es par";
else
cout<<"El numero introducido es impar";
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int edad;
cout<<"Introduzca su edad para realizar el analisis: ";
cin>>edad;
cout<<endl;
if (edad <= 10)
cout<<"Es un Nio";
else if (edad <= 17)
cout<<"Es un Adolescente";
else if (edad <= 29)
cout<<"Es un joven";
else if (edad <= 60)
cout<<"Es una persona Adulta";
else if (edad <= 100)
cout<<"Es una persona de la Tercera Edad";
else
cout<<"MUCHAS FELICIDADES!!!";
return 0;
}
129
HARDWARE - SOFTWARE
#include <iostream>
#include <cstdlib>
#include <time.h>
#include <iostream>
using namespace std;
int main()
{
int a,b,c;
cout<<"Primer valor: ";
cin>>a;
cout<<"Segundo valor: ";
cin>>b;
cout<<"Tercer valor: ";
cin>>c;
cout<<endl;
if (a>b)
130
HARDWARE - SOFTWARE
{
if (a>c)
cout<<"El mayor es: "<<a;
else
cout<<"El mayor es: "<<c;
}
else
{
if (b>c)
cout<<"El mayor es: "<<b;
else
cout<<"El mayor es: "<<c;
}
return 0;
}
9. Visualizar cuatro operaciones aritmticas de dos nmeros cualesquiera introducidos
por teclado.
#include <iostream>
using namespace std;
int main()
{
int a,b;
cout<<"Inserte primer valor: ";
cin>>a;
cout<<"Inserte segundo valor: ";
cin>>b;
cout<<endl<<endl;
cout<<"La Suma es: "<<a+b<<endl;
cout<<"La Resta es: "<<a-b<<endl;
cout<<"El producto es: "<<a*b<<endl;
cout<<"La division es: "<<a/b<<endl;
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int num;
131
HARDWARE - SOFTWARE
#include <iostream>
#include <cstdlib>
#include <time.h>
#include <iostream>
using namespace std;
int main()
{
int n;
cout<<"Inserte numero: ";
while (cin>>n && n!=0 && n>0)
{
132
HARDWARE - SOFTWARE
if (n%2==0)
cout<<"Es par"<<endl;
else
cout<<"Es impar"<<endl;
}
return 0;
}
13. Ingresar un nmero por teclado y determinar si es mltiplo de 10
#include <iostream>
using namespace std;
int main()
{
int n;
cout<<"Inserte numero: ";
cin>>n;
cout<<endl;
if (n%10==0)
cout<<"Es multiplo de 10"<<endl;
else
cout<<"No es multiplo de 10"<<endl;
return 0;
}
14. Ingresar una nota por teclado y determinar su grado de aprovechamiento (0-10)
Psimo, (11-20) Muy Malo, (21-30) Malo, (31-40) regular, (41-50) Bueno, (51-60) Muy
Bueno, (61-70) Excelente
#include <iostream>
using namespace std;
int main()
{
int nota;
cout<<"Introduzca su nota para realizar el analisis: ";
cin>>nota;
cout<<endl;
if (nota <= 10)
cout<<"Es una nota Pesima";
else if (nota <= 20)
cout<<"Es una nota Muy mala";
else if (nota <= 30)
cout<<"Es una nota Mala";
else if (nota <= 40)
133
HARDWARE - SOFTWARE
#include <iostream>
#include <cstdlib>
#include <time.h>
using namespace std;
int main()
{
int d;
srand(time(NULL));
d=1+rand()%(7-1);
cout<<d<<endl;
if(d==1 || d==6)
cout<<"Usted Gano!!!";
else
cout<<"Usted Perdio!!!!";
return 0;
}
16. Simular un juego con dos dados. Si se saca un siete o un once se gana, caso contrario se
pierde.
#include <iostream>
#include <cstdlib>
#include <time.h>
using namespace std;
int main()
{
int d1,d2;
srand(time(NULL));
d1=1+rand()%(7-1);
d2=1+rand()%(7-1);
134
HARDWARE - SOFTWARE
#include <iostream>
using namespace std;
int main()
{
int r;
cout<<"Inserte el tamao de la Serie: ";
cin>>r;
for (int i=1;i<=r;i++)
{
cout<<(i*2)-1<<" ";
}
return 0;
}
18. Visualizar la serie 1,4,7,10,13,16..n
#include <iostream>
using namespace std;
int main()
{
int r;
cout<<"Inserte el tamao de la Serie: ";
cin>>r;
for (int i=1;i<=r;i++)
{
cout<<(i*3)-2<<" ";
}
return 0;
}
135
HARDWARE - SOFTWARE
#include <iostream>
using namespace std;
int main()
{
int num;
cout<<"Inserte el numero que desa generar la
tabla"<<endl;
cin>>num;
for (int i=1;i<=10;i++)
{
cout<<num<<" * "<<i<<" = "<<num*i<<endl;
}
return 0;
}
136
HARDWARE - SOFTWARE
Dar Doble Clic en el Programa, aparecer la siguiente ventana, que es el asistente de instalacin
de Code::Blocks 10.05.
137
HARDWARE - SOFTWARE
Nos aparece una ventana con el contrato de uso del software, preguntndonos si deseamos
aceptar. Le damos Click en I Agree, que significa Acepto.
Luego nos pide la ruta donde se instalar Code::Blocks 10.05, le damos Install dejando por
defecto la direccin de instalacin.
138
HARDWARE - SOFTWARE
Nos saldr un mensaje que nos indica que el directorio de instalacin no existe, y si deseamos
crearlo. Le damos Click y le decimos que Si.
139
HARDWARE - SOFTWARE
Dos ventanas ms le damos en Next > luego en Finish. Y listo!!! Tenemos instalado Code::Blocks
10.05 en nuestro sistema.
140
HARDWARE - SOFTWARE
141
HARDWARE - SOFTWARE
En el siguiente cuadro que nos aparezca le damos clic en Next >, nos enviar a un men donde
debemos seleccin que lenguaje deseamos programar. En nuestro caso seleccionamos el que dice
C++ y le damos click en Next >.
En la siguiente ventana debemos darle el nombre del proyecto, y seleccionar en que directorio
deseamos guardar nuestro proyecto.
142
HARDWARE - SOFTWARE
Y en la ventana Final no tocamos nada, y le damos en Finish. De seguro les saldr una venta en
blanco. No hay problema. En la parte Izquierda tenemos un panel de seleccin.
Le damos doble click en el que dice: Sources nos aparecer un archivo que dice main.cpp, pues es
ah donde debemos entrar con un doble click. Y ya estomos listos para empezar!!.
143
HARDWARE - SOFTWARE
BIBLIOGRAFIA
Fundamentos de la Programacin
Schaums Outline
Addison Wesley
Accelerated C++
http://c.conclase.net
144