



























































Prepara tus exámenes y mejora tus resultados gracias a la gran cantidad de recursos disponibles en Docsity
Gana puntos ayudando a otros estudiantes o consíguelos activando un Plan Premium
Prepara tus exámenes
Prepara tus exámenes y mejora tus resultados gracias a la gran cantidad de recursos disponibles en Docsity
Prepara tus exámenes con los documentos que comparten otros estudiantes como tú en Docsity
Encuentra los documentos específicos para los exámenes de tu universidad
Estudia con lecciones y exámenes resueltos basados en los programas académicos de las mejores universidades
Responde a preguntas de exámenes reales y pon a prueba tu preparación
Consigue puntos base para descargar
Gana puntos ayudando a otros estudiantes o consíguelos activando un Plan Premium
Comunidad
Pide ayuda a la comunidad y resuelve tus dudas de estudio
Ebooks gratuitos
Descarga nuestras guías gratuitas sobre técnicas de estudio, métodos para controlar la ansiedad y consejos para la tesis preparadas por los tutores de Docsity
Estructura de datos de programacion
Tipo: Apuntes
1 / 67
Esta página no es visible en la vista previa
¡No te pierdas las partes importantes!




























































1
Objetivo
Otorgar al participante el conocimiento, la habilidad y la aptitud para: comprender y manejar las representaciones más utilizadas para el procesamiento de información en sistemas de computación. Conocer los diferentes métodos de búsqueda y ordenamiento y seleccionar y aplicar el algoritmo más adecuado para la solución a problemas de ingeniería.
Bibliografía Tenembaum, A. N. Augenstein, J. J. Estructuras de Datos en C. Prentice-Hall. México.
Ullman, J., Aho, A. y Hopcroft, J. Estructuras de Datos y Algoritmos. Addison-Wesley. México. 1988. Joyanes, L. Fundamentos de Programación. Algoritmos y estructura de datos. McGraw-Hill. México. 1990. Cairó, Osvaldo. Estructuras de datos. McGraw-Hill. México. 1993.
2
Un apuntador es una variable que contiene una dirección de memoria. Esta dirección puede ser la posición de otra variable en la memoria.
Por ejemplo:
int edad, *aEdad;
Edad=26;
AEdad=&Edad;
En el ejemplo tenemos dos variables una de tipo entero llamada Edad y otra de tipo apuntador a entero llamada aEdad. Posteriormente Edad toma el valor de 26 y aEdad toma como valor la dirección de la variable Edad (0456). Podemos decir entonces que aEdad apunta a Edad, y a través de aEdad puedo modificar el valor de la variable Edad.
La declaración de un apuntador como ya vimos en el ejemplo se hace:
tipo *nombre_var;
Se agrega * como prefijo al declarar una variable de tipo apuntador. Tipo define el tipo de dato al que va a hacer referencia el apuntador.
26
Edad
0456
aEdad
[0456] [0714]
Nombre de la variable entera edad
Dirección de memoria de la variable edad
Nombre de la variable apuntador a entero
Dirección de memoria de la variable aEdad
Contenido de la variable edad
Contenido de la variable apuntador aEdad (dirección de la variable Edad
4
Las únicas operaciones que se pueden realizar con variables de apuntador son la suma y la resta, de manera que los operadores válidos son:
− resta
++ incremento
-- decremento
La aritmética de operadores no suma las direcciones de memoria, sino elementos. Esto quiere decir que si yo incremento en uno a una variable de apuntador a entero no se incrementara un byte, sino dos bytes porque es el espacio que ocupa un entero en memoria.^2
Más ejemplos con apuntadores:
int x=10, y=2, z[14], *p;
p=&x; p apunta ahora a la variable x
y=*p; y contiene ahora el valor de 10
*p=0; x es asignada con un valor de cero a través del apuntador p.
p=&z[2]; p apunta ahora a z[2]
p = p+2; incrementa en dos lo apuntado por p.
++*p incrementa en uno lo apuntado por p.
(*p)++ incrementa en uno lo apuntado por p. los paréntesis son necesarios para que no incremente a p.
(^2) Es lógico que así funcione ya que lo práctico aquí es apuntar al siguiente entero (por ejemplo recorrido de un arreglo).
5
La estructura en C es muy similar , en concepto, al registro en PASCAL, FORTRAN, etc.
Una definición de estructura forma una plantilla o patrón que puede utilizarse para crear variables de estructura que, con diferentes nombres, se ajusten a esa plantilla.
El formato de definición de una variable estructura es el siguiente:
struct NombreEstructura { TipoVariable1 NombreVariable1 ; TipoVariable2 NombreVariable2 ; TipoVariable3 NombreVariable3 ; ΜΜΜΜ TipoVariableN NombreVariableN ; } NombVarEstru1, NombVarEstru2, ΚΚΚΚ, NombVarEstruN ;
o bien:
struct NombreEstructura { TipoVariable1 NombreVariable1 ; TipoVariable2 NombreVariable2 ; TipoVariable3 NombreVariable3 ; ΜΜΜΜ TipoVariableN NombreVariableN ; } ;
struct NombreEstructura NombVarEstru1, NombVarEstru2, ΚΚΚΚ, NombVarEstruN ;
Si solamente se necesita una variable estructura, por ejemplo NombVarEstru1, no es necesario poner el nombre de la estructura (NombreEstructura) y quedaría el siguiente formato:
struct { TipoVariable1 NombreVariable1 ; TipoVariable2 NombreVariable2 ; TipoVariable3 NombreVariable3 ; ΜΜΜΜ TipoVariableN NombreVariableN ; } NombVarEstru1 ;
Una estructura es un conjunto de variables que se citan y manejan con el mismo nombre y que permite además la utilización individual de sus elementos.
7
Una estructura puede anidar otras estructuras, previamente definidas, como se demuestra en el siguiente ejemplo en el que las estructuras se definen a nivel global.
Para mencionar un elemento de la estructura se indican, ordenadamente, el nombre de la estructura principal, el nombre de la estructura anidada y el nombre del elemento de la estructura, todos ellos separados por el operador punto (.)
#include <stdio.h> #include <conio.h> struct autor /* Declara GLOBALMENTE autor /{ char nomb[25]; char ape[35]; }; struct tema / Declara GLOBALMENTE tema /{ char modulo[4]; char area[20]; }; / Estructura "libros" que anida las estructuras " autor ”y " tema ". La declaración es también GLOBAL. / struct libros{ char nom_lib[70]; struct autor aut; struct tema tem; }; void main(){ struct libros li; / Declara li del tipo libros / clrscr(); / Borra la pantalla / puts("Titulo del libro"); / Imprime en pantalla / gets(li.nom_lib); / Lee datos del teclado */ puts("Apellidos del autor"); gets(li.aut.ape); puts("Nombre del autor"); gets(li.aut.nomb); puts("Modulo:"); gets(li.tem.modulo); puts("Area de conocimiento"); gets(li.tem.area); }
El anidamiento de estructuras gráficamente se visualizaría así:
nom_lib nomb ape modulo area aut tem li
8
De igual forma que las variables, también una estructura puede formar parte de un arreglo. Para ello, primero debemos definir la estructura y a continuación declarar una variable arreglo de dicho tipo.
El formato general es el siguiente:
struct NombreEstructura { TipoVariable1 NombreVariable1 ; ΜΜΜΜ TipoVariableN NombreVariableN ; } ;
struct NombreEstructura VariableArreglo[NumElementos];
Se utiliza el operador punto para citar elementos individuales de la variable estructura e incluso elementos individuales del arreglo utilizando el subíndice correspondiente.
Como cualquier otra variable, una estructura puede tener un almacenamiento global , local o externo.
Ejemplo en C:
struct libreria /* Comienzo de definición del “patrón”. / { char titulo[100]; char autor[75]; unsigned long precio; }; / Fin de definición del “patrón. */
void main() /* Inicio de la función main() / { / Declara la variable libros[NumElementos] de “tipo” librería */
struct libreria libros[10];
int n, i; Λ Λ
/* Se captura la información de n libros */
printf ("Cuantos libros: \n"); scanf ("%d", &n); fflush(stdin); for (i=0; i<n; i++) {
10
Muchos compiladores, basados en el lenguaje C original definido por Kernighan y Ritchie, no permiten asignación de estructuras. Por tanto, sería necesario asignar explícitamente cada elemento de una estructura a otra.
Tampoco es posible comparar la igual de dos estructuras en una sola operación en C. Es decir, libro_comp == libro_mat no está definido.
Para pasar una estructura a una función, debemos pasar su dirección a la función y referirse a la estructura mediante un apuntador , es decir, paso de parámetros por referencia.
Para referirse a un elemento de una estructura pasada como parámetro por referencia se utiliza el operador -> (guión medio y un símbolo de mayor).
Existen dos operadores de selección para tener acceso a una estructura:. y ->
. Selector de miembro directo -> Selector de miembro indirecto o apuntador
Los operadores de selección. y -> se usan para accesar los miembros de una estructura y de una unión.
Suponga que el objeto s es de tipo estructura S y sptr es un apuntador a S. Entonces, si m es un identificador de miembro de tipo M declarado en S , estas expresiones:
s.m sptr->m
son de tipo M , y ambas representan el objeto miembro m en s.
La expresión
sptr->m
es un sinónimo conveniente para (sptr).m*
Selector de miembro directo (. )
Sintaxis: expresión_postfija. identificador
11
Operador de miembro indirecto ( -> )
Sintaxis: expresión_postfija -> identificador
La expresión designa a un miembro objeto de una estructura o unión. El valor de la expresión es el valor del miembro seleccionado.
Por ejemplo,
struct mi_estructura { int i; char cadena[21]; double d; } s, *sptr=&s;
...
s.i = 3; // asigna al miembro i de mi_estructura s sptr->d = 1.23; // asigna al miembro d de mi_estructura s
13
#include <stdio.h>
void main() {
union tipos { unsigned char car[6]; int entero; };
union tipos t;
t.entero = 256;
printf(“\n\nEl espacio reservado para CADA variable ASOCIADA a la union es”); printf(“de %d bytes. \n\n”, sizeof(union tipos)); printf(“Las direcciones de memoria de los elementos INTEGRADOS en la “); printf(“ UNION son: \n\n”); printf(“&t.ncar[0] = %u”, &t.car[0]); printf(“\n&t.entero = %u\n\n”, &t.entero); printf(“El byte \”0\” de %d es %d\n”, t.entero, t.car[0]); printf(“El byte \”1\” de %d es %u\n”, t.entero, t.car[1]); }
Dirección 4296
Número decimal 256 en binario
byte 0 byte 1 byte 2 byte 3 byte 4 byte 5
t.entero
t.car
14
Una pila es un conjunto ordenado de elementos en el cual se pueden agregar y eliminar elementos en un extremo, que es llamado el tope de la pila.
La definición de la pila considera la inserción y eliminación de elementos, por lo que una pila es un objeto dinámico en constante cambio.
La definición especifica que un solo extremo de la pila se designa como el tope.
Pueden colocarse nuevos elementos en el tope de la pila o se pueden quitar elementos.
Una pila con 6 elementos
➜ G F (^) ➜ F (^) ➜ K E E (^) ➜ E E (^) ➜ E D D D D D (^) ➜ D (^) ➜ G C C C C C C ➜ C C B B B B B B B B A A A A A A A A
pop (s) pop (s) push (s, K) pop (s) pop (s) pop (s) push (s, G)
La característica más importante de una pila es que el último elemento insertado en ella es el primero en suprimirse. Por esta razón, en ocasiones una pila se denomina una lista “último en entrar, primero en salir” o LIFO ( l ast i n, f irst o ut).
En la pila no se conserva un registro de los elementos intermedios que han estado en ella, si se desea conservar, debe llevarse en otra parte.
16
Hay varias formas de representar una pila en C. Por el momento, consideraremos la más simple de ellas. Cada una tiene ventajas y desventajas, en términos de la fidelidad con la que refleja el concepto abstracto de una pila y el esfuerzo que deben aportar el programador y la computadora para usarla.
Una pila en C se declara como una estructura que contiene dos objetos : un arreglo para contener los elementos de la pila y un entero para indicar la posición del tope de la pila actual dentro del arreglo.
#define STACKSIZE 100 struct stack { int top; int items[STACKSIZE]; };
Una vez que se ha hecho esto, de declara una pila real s mediante
struct stack s;
Aquí suponemos que los elementos de la pila s contenidos en el arreglo s.items son enteros y que la pila en ningún momento contendrá más enteros que STACKSIZE.
El identificador top siempre debe declararse como entero, dado que su valor representa la posición dentro del arreglo items del elemento en el tope de la pila.
La pila vacía no contiene elementos y, por tanto, se indica mediante top igual a –1. Para inicializar una pila s para un estado vacío, se ejecuta al inicio s.top=-1 :
int empty ( struct stack ps) { if (ps->top == -1) return (TRUE); else return (FALSE); } / fin de empty */
En funciones como pop y push , las cuales modifican sus argumentos de estructura, al igual que empty (que no lo hace) adoptamos la convención de pasar la dirección de la estructura de la pila.
17
Implementación de la operación pop
La posibilidad de un subdesbordamiento debe considerarse al implementar la operación pop , dado que el usuario puede intentar remover un elemento de una pila vacía. La función pop ejecuta las acciones siguientes:
int pop ( struct stack ps) { if (empty (ps)) { printf (“stack underflow”); exit (1); } / fin del if / return (ps->items[ps->top--]); } / fin de pop */
La llamada a la función pop sería así: x = pop (&s);
if (!empty (&s)) x = pop (&s); else /* realiza acciones correctivas */
Implementación de la operación push
void push ( struct stack ps, int x) { if (ps->top == STACKSIZE-1) { printf ( “stack overflow”); exit (1); } else ps->items[++(ps->top)] = x; return ; } / fin de push */
La llamada a la función push sería: push (&s, x);
19
Una cola es un conjunto ordenado de elementos del que pueden suprimirse elementos de un extremo (llamado la parte delantera de la cola) y en el que pueden insertarse elementos del otro extremo (llamado la parte posterior de la cola).
El primer elemento insertado en una cola es el primer elemento que se suprime.
Por esta razón, una cola se denomina una lista fifo (el primero en entrar, el primero en salir) que es lo contrario de una pila, la cual es una lista lifo (el último en entrar, el primero en salir).
Parte delantera
Parte posterior
Parte delantera
Parte posterior
Parte delantera
Parte posterior
Se aplican tres operaciones primitivas a una cola:
Podría considerarse una operación adicional que indique si la cola se encuentra llena.
La operación insert puede ejecutarse siempre, pues no hay un límite -teórico- en la cantidad de elementos que puede contener una cola. Sin embargo, la operación remove sólo puede aplicarse si la cola no está vacía; no hay forma de remover un elemento de una cola que no contiene elementos. El resultado de un intento no válido de remover un elemento de una cola vacía se denomina subdesbordamiento. La operación empty siempre es aplicable.
20
Una idea es usar un arreglo para contener los elementos de la cola y emplear dos variables, front (parte delantera) y rear (parte posterior) para contener las posiciones dentro del arreglo de los elementos primero y último de la cola.
#define MAXQUEUE 100 struct queue { int items [MAXQUEUE]; int front, rear; };
Usar un arreglo para que contenga una cola introduce la posibilidad de desbordamiento , si la cola llega a ser más grande que el tamaño del arreglo.
La operación insert (q, x), podría implementarse mediante la sentencia
q.items[++q.rear] = x;
y la operación x = remove (q) mediante
x = q.items[q.front++];
Al principio, se establece q.rear en –1 y q.front en 0.
La cola está vacía cada vez que q.rear < q.front. La cantidad de elementos en la cola en cualquier momento es igual al valor de q.rear – q.front + 1.