Está en la página 1de 62

GRADO EN INGENIERÍA DE COMPUTADORES

2022-2023
Estructuras de Datos
Índice
Parte I. Tipos Abstractos de Datos y Algoritmia

Tema 1. Tipos Abstractos de Datos y Algoritmia

Parte II.- Estructuras de datos lineales

Tema 2. Listas, pilas, colas y conjuntos

Parte III.- Estructuras de datos no lineales

Tema 3. Árboles.

Tema 4. Grafos.

2
IEEE spectrum popularity ranking

URJC-ED-Introducción
3
IEEE spectrum popularity ranking

URJC-ED-Introducción
4
RedMonk ranking

URJC-ED-Introducción 5
Índice Tiobe

URJC-ED-Introducción
6
Índice Tiobe

URJC-ED-Introducción
7
1.1 Normas de estilo
1.2 Repaso (rápido) de C
1.3 Punteros
1.4 Complejidad

URJC-ED-Introducción
8
Normas de estilo C
• Evitar varias instrucciones en una misma línea
• Tabular adecuadamente el anidamiento de sentencias. Evitar
escribir:
a=33; b++; c = a + b + 44;

• En la asignatura todos los bloques tendrán las llaves de


apertura y cierre (aunque tengan una sola instrucción):

if (precio>MAXIMO)
{
printf(“Es muy caro\n”);
}

URJC-ED-Introducción
9
Normas de estilo
• Las llaves de comienzo y fin de bloque es obligatorio que
aparezcan en una línea sin más sentencias:

- Correcto:

if (precio>MAXIMO)
{
printf(“Abusivo\n”);
}

- Incorrecto:
if (precio>MAXIMO) {
printf(“Abusivo\n”); }

URJC-ED-Introducción 10
Normas de estilo
* Dar nombres nemotécnicos a los identificadores que describan
lo mejor posible su cometido o lo que representan
(subprogramas y variables).
* Palabras reservadas: MINUSCULAS

while, for, if, do, switch, ...

URJC-ED-Introducción
11
Normas de estilo
* Identificadores: descriptivos y minúsculas
* Palabras separadas, unidas por _ o utilizando primer carácter
de la palabra sufija mayúscula o ambos
- nombre_archivo, nombreArchivo, nombre_Archivo
* Constantes: MAYUSCULAS
- IVA, PI, NUMERO_E,...
* Procedimientos y funciones: Empiezan por letra MINUS.
- busquedaBinaria, apilar, pilaVacia,...
* Tipos: Empezando por MAYUS.
-Pila, Elemento, IteradorVector, ...

URJC-ED-Introducción
12
Normas de estilo

* Módulos y Ficheros:
- Los nombres de programas y módulos (clases):
· Deben coincidir con los nombres de los ficheros que los contienen.
· Si hay más de una palabra en el nombre, el fichero separará las palabras por “_”
· Deben ir en minúsculas

lista.c, elemento_puntero_int.c,
iterador_vector.h, ...

URJC-ED-Introducción
13
Normas de estilo
* Módulos y Ficheros:
- Se recomienda que contengan una cabecera de identificación como esta:

/**
*
* Módulo: Nombre
* Fichero: ( ) Programa ( ) Espec. TAD ( ) Impl. TAD ( ) Otros
* Autor(es): Nombre(s)
* Fecha: Fecha de actualización
*
* Descripción:
* Breve descripción del módulo (párrafo corto)
*
*/

URJC-ED-Introducción
14
Normas de estilo

* Se recomienda emplear subprogramas para tareas bien


identificadas (abstracción procedimental).
* Emplear sentencias de repetición (especialmente bucles for y
while) cuando sea posible.
* Prohibido el uso de variables globales en subprogramas.
* Uso adecuado de funciones (devuelven un valor).

URJC-ED-Introducción
15
1.1 Normas de estilo
1.2 Repaso (rápido) de C
1.3 Punteros
1.4 Complejidad

URJC-ED-Introducción
16
Arrays en C

* Una variable de tipo de dato ARRAY se define como


sigue:

int a_int[3]; // a_int es un array de 3 enteros


float a_float[3]; // a_float es un array de 3 reales en
// precisión simple
char matriz[3][7] // m_char es un array bidimensional de
// 3x7 elementos.

URJC-ED-Introducción
17
Arrays en C (cont.)

* Son estructuras de datos:


- De acceso directo: permiten almacenar y recuperar
directamente los datos especificando su posición dentro de la
estructura.
- Homogéneas: sus elementos son TODOS del MISMO TIPO.
- Estáticas: su tamaño se establece de forma FIJA cuando se
declara variable de este tipo y no puede cambiar su tamaño
durante la ejecución del programa.

URJC-ED-Introducción
18
Registros en C (struct)

* Son estructuras de datos:


- Heterogéneas: sus elementos pueden ser de DISTINTOS tipos.
- Estáticas: su tamaño se establece de forma FIJA cuando se
declara variable de este tipo.

struct NombreRegistro
{
IdTipo1 idCampo1;
idTipo2 idCampo2;
...
idTipon idCampon;
};
NombreRegistro reg1; // Declaración de variable
URJC-ED-Introducción
19
Conceptos aprendidos: Registros (cont.)

* Para acceder a un campo se usa el operador punto (.):


nombreVariableRegistro.nombreCampo

struct Ficha
{
char* nombre;
int edad;
int float;
};

void main()
{
Ficha p;
// ...
printf(“%s %d %f”, p.nombre, p.edad, p.sueldo);
}

URJC-ED-Introducción
20
1.1 Normas de estilo
1.2 Repaso (rápido) de C
1.3 Punteros
1.4 Complejidad

URJC-ED-Introducción
21
Memoria dinámica y punteros

* Las estructuras de datos que conocemos (arrays,


struct, integer, …) son de memoria estática.

* Cuando no se sabe cuánta memoria se va a necesitar


para una estructura de datos en ejecución, el programa
pedirá (y devolverá) al sistema memoria dinámicamente
(cuando sea necesario).

* La gestión de memoria dinámica en muchos lenguajes de


programación se realiza a través de punteros.

URJC-ED-Introducción
22
Punteros

* Una variable puntero sirve para albergar la dirección de


memoria de otra variable (es decir, apunta a otra variable).
* Utilizando punteros podremos:
- Pedir (reservar) nueva memoria dinámica al sistema.
- Liberar la memoria a la que se apunta.
- Acceder al valor del dato al que se apunta.

* C (como C++, Ada, Pascal y otros lenguajes) posee


mecanismos para manejar memoria dinámica a través de
punteros.

URJC-ED-Introducción
23
Declaración de enteros en C

•Recordemos como declarar una variable entera x:

int x; // reserva memoria para un dato entero

El compilador de C reservará N bytes en una posición de memoria a la


que llamará x.

URJC-ED-Introducción
24
Declaración de punteros en C

* Declaración de un tipo y una variable p puntero a un


entero:

int* p; // reserva una celda de memoria que pueda


// contener la dirección de memoria de una
// variable entera

* El compilador de C reservará D bytes (los necesarios para almacenar


una dirección de memoria) en una posición de memoria a la que
llamará p.

URJC-ED-Introducción
25
El puntero a “ninguna parte”

* NULL es una constante (= 0) de tipo entero que indica


que el puntero no apunta a ninguna celda de memoria:

int* p; // no está inicializada, contiene un


// valor indeterminado que no es NULL

p = NULL; // p no apunta a ninguna dirección

Gráficamente: p distinto de dir. no asignada: p ?

URJC-ED-Introducción
26
Operaciones con punteros

* Comparación ( == y != ) y asignación ( = ) de punteros.

* Dirección de una variable v: &v


- Se obtiene la dirección de memoria de una variable para asignarla a
un puntero a ese tipo de variable.

* Contenido de un puntero p: *v ó v->


- Se accede a la variable apuntada por el puntero (a su contenido).

URJC-ED-Introducción
27
Ejemplo de operaciones con punteros

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

int main()
{
int i;
int* p;

p = NULL;
i = 7;
p = &i; // Asignar la dirección de la variable i a p.
*p = 3; // Cambiar el contenido de lo apuntado por p.
printf(“%d\n”,i); // Escribe el valor 3.
}

URJC-ED-Introducción
28
Ejemplo de operaciones con punteros

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

int main()
{
int i;
int* p;

p = NULL;
i = 7;
p = &i; // Asignar la dirección de la variable i a p.
*p = 3; // Cambiar el contenido de lo apuntado por p.
printf(“%d\n”,i); // Escribe el valor 3.
}

URJC-ED-Introducción
29
Ejemplo de operaciones con punteros

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

int main()
{
int i;
int* p;

p = NULL;
i = 7;
p = &i; // Asignar la dirección de la variable i a p.
*p = 3; // Cambiar el contenido de lo apuntado por p.
printf(“%d\n”,i); // Escribe el valor 3.
}

Dir. Memoria: 38
i ?

URJC-ED-Introducción
30
Ejemplo de operaciones con punteros

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

int main()
{
int i;
int* p;

p = NULL;
i = 7;
p = &i; // Asignar la dirección de la variable i a p.
*p = 3; // Cambiar el contenido de lo apuntado por p.
printf(“%d\n”,i); // Escribe el valor 3.
}

Dir. Memoria: 90 38
p ? i ?

URJC-ED-Introducción
31
Ejemplo de operaciones con punteros

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

int main()
{
int i;
int* p;

p = NULL;
i = 7;
p = &i; // Asignar la dirección de la variable i a p.
*p = 3; // Cambiar el contenido de lo apuntado por p.
printf(“%d\n”,i); // Escribe el valor 3.
}

Dir. Memoria: 90 38
p i ?

URJC-ED-Introducción
32
Ejemplo de operaciones con punteros

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

int main()
{
int i;
int* p;

p = NULL;
i = 7;
p = &i; // Asignar la dirección de la variable i a p.
*p = 3; // Cambiar el contenido de lo apuntado por p.
printf(“%d\n”,i); // Escribe el valor 3.
}

Dir. Memoria: 90 38
p i 7

URJC-ED-Introducción
33
Ejemplo de operaciones con punteros

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

int main()
{
int i;
int* p;

p = NULL;
i = 7;
p = &i; // Asignar la dirección de la variable i a p.
*p = 3; // Cambiar el contenido de lo apuntado por p.
printf(“%d\n”,i); // Escribe el valor 3.
}

Dir. Memoria: 90 38
p 38 i 7
Se dice que “p apunta a i”

URJC-ED-Introducción 34
Ejemplo de operaciones con punteros

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

int main()
{
int i;
int* p;

p = NULL;
i = 7;
p = &i; // Asignar la dirección de la variable i a p.
*p = 3; // Cambiar el contenido de lo apuntado por p.
printf(“%d\n”,i); // Escribe el valor 3.
}

Dir. Memoria: 90 38
p 38 i 3
Se dice que “p apunta a i”

URJC-ED-Introducción 35
Ejemplo de “operación ilegal”
#include <stdio.h>
#include <stdlib.h>

int main()
{
int i;
int* p;

p = NULL;
p = &i; // Asignar la dirección de la variable i a p
*p = 3; // Almacenar el valor 3 en la celda de memoria
// apuntada por p

p = 3; // Operación ilegal: 3 es integer mientras que


// p es de tipo puntero y sólo puede contener una
// dirección de memoria de una variable integer
}

URJC-ED-Introducción
36
Ejemplo de “operación ilegal”
#include <iostream>
#include <stdlib.h>

int main()
{
int i;
int* p;

p = NULL;
p = &i; // Asignar la dirección de la variable i a p
*p = 3; // Almacenar el valor 3 en la celda de memoria
// apuntada por p

p = 3; // Operación ilegal: 3 es integer mientras que


// p es TipoPuntero y sólo puede contener una
// dirección de memoria de una variable integer
}
El compilador responde:
In function 'int main()': 13:5: error: invalid conversion from 'int' to 'int*' [-fpermissive]
URJC-ED-Introducción
37
Acceso a ARRAYs como punteros

* Una variable ARRAY representa también la dirección al


primer elemento del ARRAY:
#include <iostream>
#include <stdlib.h>

int main()
{
// Decl. array de 5 enteros e inicialización.
int a[5] = {1, 2, 3, 4, 5};

// Sintaxis de acceso común:


std::cout << "a[4]=" << a[4] << std::endl;
std::cout << "a[0]=" << a[0] << std::endl;

// Sintaxis de acceso como puntero:


std::cout << "*a=" << *a << std::endl;
std::cout << "*(a+4)=" << *(a+4) << std::endl;
}
URJC-ED-Introducción
38
Punteros a ARRAY
#include <stdio>
#include <stdlib.h>

int main()
{
// Decl. array de 5 enteros e inicialización.
int a[5] = {1, 2, 3, 4, 5};
int* b = a;

// Sintaxis de acceso común:


printf("b[0]=%d\n“,b[0]);
printf("b[0]=%d\n“,b[1]);
printf("b[0]=%d\n“,b[2]);
printf("b[0]=%d\n“,b[3]);

// Sintaxis de acceso como puntero:


printf(“*b=%d\n“,*b);
// Aritmética de punteros
printf(“*b+1=%d\n“,*(b+1));
printf(“*b+2=%d\n“,*(b+2));
printf(“*b+3=%d\n“,*(b+3));
} URJC-ED-Introducción 39
Operador “contenido” *: struct
#include <stdio.h>
#include <stdlib.h>

struct Registro
{
int dato;
int indice;
};

int main()
{
Registro r;
Registro* p;

p = &r; // Asignar la dirección de r a p.


(*p).dato = 5; // Acceso a r a través de p.
(*p).indice = 33; // Acceso a r a través de p.

printf("r.dato=%d\n“,r.dato);
printf("r.indice=%d\n“,r.índice);
} URJC-ED-Introducción
40
Operador “contenido” ->: struct
#include <stdio.h>
#include <stdlib.h>

struct Registro
{
int dato;
int indice;
};

int main()
{
Registro r;
Registro* p;

p = &r; // Asignar la dirección de r a p.


p->dato = 5; // Acceso a r a través de p.
p->indice = 33; // Acceso a r a través de p.

printf("r.dato=%d\n“,r.dato);
printf("r.indice=%d\n“,r.índice);
}
URJC-ED-Introducción 41
Ejercicio 1

*¿Qué valores escribe el programa?


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

int main()
{
int a, b;
int* pb;

a = 10;
pb = &a;
b = *pb;
printf(“%d %d”,a,b);
}

URJC-ED-Introducción
42
Ejercicio

*¿Qué valores escribe el programa?


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

int main()
{
int a, b;
int* pb;

a = 10;
pb = &a;
printf(“%d\n”,*pb);
*pb=7;
printf(“%d\n”,a);
}

URJC-ED-Introducción
43
Petición de nueva memoria dinámica

type* malloc (size) reserva tantas nuevas celdas


de memoria dinámica como marque dim e inicializa la
variable puntero tal que apunte a esta nueva celda.

int* p = (int*) malloc (sizeof(int));

• Si p es un puntero a int, reserva espacio para


contener un int.
• Si p hubiese sido un puntero a char, reservaría
espacio para albergar un valor de tipo char.

URJC-ED-Introducción
44
Semántica del procedimiento malloc

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

int main()
{
int* p;

p = (int*) malloc (sizeof(int));


}

URJC-ED-Introducción
45
Semántica del procedimiento malloc

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

int main()
{
int* p;

p = (int*) malloc (sizeof(int));


}

Dir. Memoria: 90
p ?

URJC-ED-Introducción
46
Semántica del procedimiento malloc

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

int main()
{
int* p;

p = (int*) malloc (sizeof(int));


}
Memoria dinámica (del
heap o montón)”
Dir. Memoria: 90 54
p 54 p ? Es la única manera que
tenemos de acceder a lo que
hay almacenado en la
Se dice que “p apunta a un entero” posición 54

URJC-ED-Introducción
47
Liberación de memoria dinámica

*free libera el espacio de memoria indicado por la


variable puntero que se encuentra a continuación:

int* p = (int*) malloc(sizeof(int));


// …
free(p);

// Una referencia posterior a *p puede ser desastrosa,


// pues p apuntará a una celda que ya no está reservada
//
// Conviene asignarle null
p = NULL;

URJC-ED-Introducción
48
Semántica del procedimiento free

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

int main()
{
int* p;

int* p = (int*) malloc(sizeof(int));


free(p);
p = NULL
}

URJC-ED-Introducción
49
Semántica del procedimiento free

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

int main()
{
int* p;

int* p = (int*) malloc(sizeof(int));


free(p);
p = NULL;
}

Dir. Memoria: 90
p ?

URJC-ED-Introducción
50
Semántica del procedimiento free

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

int main()
{
int* p;

int* p = (int*) malloc(sizeof(int));


free(p);
p = NULL;
}
Dir. Memoria: 90 54 Memoria dinámica (del
heap o montón)”
p 54 p ?

Se dice que “p apunta a un entero”

URJC-ED-Introducción
51
Semántica del procedimiento delete

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

int main()
{
int* p;

int* p = (int*) malloc(sizeof(int));


free(p);
p = NULL;
}

Dir. Memoria: 90 54 Memoria dinámica (del


heap o montón)”
p 54 p ?
Marcada como libre

Se dice que “p apunta a un entero”


URJC-ED-Introducción
52
Semántica del procedimiento delete

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

int main()
{
int* p;

int* p = (int*) malloc(sizeof(int));


free(p);
p = NULL
}

Dir. Memoria: 90
p

Se dice que “p apunta a un entero”

URJC-ED-Introducción
53
Nueva memoria dinámica: ARRAYs

* T* malloc (N*sizeof(T)) reserva memoria


dinámica para almacenar N variables de tipo T e inicializa
la variable puntero tal que apunte a la primera celda
reservada.
int* p = (int*)malloc(5*sizeof(int))

* Si p es un puntero a int reserva espacio para contener


N int. Si p es un puntero a char reserva espacio para
albergar N valores de tipo char.

URJC-ED-Introducción
54
Liberación de m. dinámica: ARRAYs

*free también libera el espacio de memoria indicado por


la variable puntero para la que se reservó un ARRAY de
elementos:

int* p = (int*) malloc(5*sizeof(int));


// …
free(p);

// Una referencia posterior a *p puede ser desastrosa,


// pues p apuntará a una celda que ya no está reservada
//
// Conviene asignarle nullptr
p = NULL;

URJC-ED-Introducción
55
Ejemplo malloc y free

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

int main()
{
int* a= (int*) malloc (3*sizeof(int));

*a = 1;
*(a+1) = 2;
*(a+2) = 3;

printf("a[0]= %d\n“,*a);
printf("a[2]= %d\n“,*(a+2));

free(a); // Importante para liberar todo el ARRAY.


a = NULL;

//Esto está mal, aunque puede ser que no te dé error


printf("a[2]= %d\n“,*(a+2));
}
URJC-ED-Introducción
56
Al trabajar con punteros hay que …

•Declarar una variable de tipo puntero.


•Si es necesario reservar memoria en tiempo de ejecución
llamar a malloc.
•Después de llamar a malloc el valor del contenido del
puntero es indefinido (es necesario asignarle un valor).
•variable_puntero es diferente de: *variable_puntero
•Es necesario liberar el espacio de memoria (free)
cuando no se vaya a utilizar más el dato apuntado.

URJC-ED-Introducción
57
Perdida de memoria (memory leak)

• La memoria disponible en el heap es limitada y cada vez


que se llama a malloc una parte de esa memoria se
marca como ocupada.
• Cada vez que se llama a free, la memoria ocupada se
marca como libre.

Cuando se pide memoria con “p = malloc …” y se


asigna otra dirección a p antes de hacer un “free p”
se produce una pérdida de memoria (la memoria
apuntada por p queda ocupada e inaccesible).

URJC-ED-Introducción
58
Error de acceso

* Cada vez que se llama a “free(p)”, la memoria


ocupada se marca como libre pero p mantiene la dirección
anterior al free.

Inmediantamente después de “free p”un acceso al


contenido de p, *p o p->campo, puede producir un
error de ejecución (un error en la asignatura: SEGURO).

URJC-ED-Introducción
59
Punteros en llamadas a subprogramas

* Se pueden devolver como resultado de una función:

int main()
int* f2(int a) {
{ int* p = f2(4);
int* q =
(*int)malloc(sizeOf(in printf(“%d”,*p);
t));
*q = 34; free(p);
return q; }
}

URJC-ED-Introducción 60
Punteros en llamadas a subprog. (II)
* El paso de punteros por valor equivale al paso por
referencia de la variable que apuntan.
– El subprograma recibe una copia del puntero y apunta a
la misma información que el original,
– Un cambio en esa información es un cambio en la
apuntada por el puntero original.

void ej3(int* p1)


{ /*…..*/
}

int main()
{
int* p; int i;
i = 2; p=&i;
ej3 (p);
}
URJC-ED-Introducción 61
Ejercicio
* ¿Qué valores escribe el programa?
#include <stdio.h>
#include <stdlib.h>

void f(int* x, int y, int& z)


{
*x = (*x) * (*x);
y = y * y;
z = z * z;
}

int main()
{
int a, b, c;

a = 2;
b = 3;
c = 4;
f(&a, b, c);
printf(“a= %d b=%d c=%d”,a,b,c);
}
URJC-ED-Introducción 62

También podría gustarte