Docsity
Docsity

Prepara tus exámenes
Prepara tus exámenes

Prepara tus exámenes y mejora tus resultados gracias a la gran cantidad de recursos disponibles en Docsity


Consigue puntos base para descargar
Consigue puntos base para descargar

Gana puntos ayudando a otros estudiantes o consíguelos activando un Plan Premium


Orientación Universidad
Orientación Universidad


Algoritmos de Ordenación y Búsqueda en C: Ejercicios y Ejemplos, Apuntes de Programación C

Programacion en C++ ejercicios practicos

Tipo: Apuntes

2019/2020

Subido el 22/03/2022

crm-sede
crm-sede 🇲🇽

5

(2)

5 documentos

1 / 30

Toggle sidebar

Esta página no es visible en la vista previa

¡No te pierdas las partes importantes!

bg1
2012
Asignatura: Algoritmo y Estructura de Datos
Departamento de Computación – UNAN León
ALGORITMOS DE ORDENA
CIÓN Y
BÚSQUEDA EN C
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e

Vista previa parcial del texto

¡Descarga Algoritmos de Ordenación y Búsqueda en C: Ejercicios y Ejemplos y más Apuntes en PDF de Programación C solo en Docsity!

Asignatura: Algoritmo y Estructura de Datos Departamento de Computación – UNAN León

ALGORITMOS DE ORDENACIÓN Y

BÚSQUEDA EN C

TEMA 1: ALGORITMOS DE ORDENACIÓN Y BÚSQUEDA

1.1 INTRODUCCIÓN:

Uno de los procedimientos más comunes y útiles en el procesamiento de datos, es la clasificación u ordenación de los mismos. Se considera ordenar al proceso de reorganizar un conjunto de objetos en una secuencia determinada. El objetivo de este proceso generalmente es facilitar la búsqueda de uno o más elementos pertenecientes a un conjunto.

Ordenar es simplemente colocar información de una manera especial basándonos en un criterio de ordenamiento. En la computación el ordenamiento de datos también cumple un rol muy importante, ya sea como un fin en sí o como parte de otros procedimientos más complejos.

1.2 CONCEPTOS PRELIMINARES:

  • Clave: La parte de un registro por la cual se ordena la lista. Por ejemplo, una lista de registros con campos nombre, dirección y teléfono se puede ordenar alfabéticamente de acuerdo a la clave nombre. En este caso los campos dirección y teléfono no se toman en cuenta en el ordenamiento.
  • Criterio de ordenamiento (o de comparación): EL criterio que utilizamos para asignar valores a los registros con base en una o más claves. De esta manera decidimos si un registro es mayor o menor que otro.
  • Registro: Un grupo de datos que forman la lista. Pueden ser datos atómicos (enteros, caracteres, reales, etc.) o grupos de ellos, que en C equivalen a las estructuras.

1.3 ORDENAMIENTO DE DATOS:

Por lo general, todos los algoritmos de ordenación funcionan de una forma similar. Toman una lista de elementos, en nuestro caso un array, comparan sus elementos siguiendo una estrategia definida y, según el resultado de dicha comparación, mueven los datos de un lugar a otro hasta conseguir una lista (array) final ordenado.

La ordenación o clasificación de datos ( sort , en inglés) es una operación consistente en disponer un conjunto de datos en algún determinado orden con respecto a uno de los campos de elementos del conjunto. Por ejemplo, cada elemento del conjunto de datos de una guía telefónica tiene un campo nombre, un campo dirección y un campo número de teléfono; la guía telefónica está dispuesta en orden alfabético de nombres; los elementos numéricos se pueden ordenar en orden creciente o decreciente de acuerdo al valor numérico del elemento. En terminología de ordenación, el elemento por el cual está ordenado un conjunto de datos (o se está buscando) se denomina clave.

1.5 ORDENAMIENO POR MÉTODO DE LA BÚRBUJA (BUBBLESORT):

Hay muchas formas de clasificar datos y una de las más conocidas es la clasificación por el método de la burbuja. Es uno de los más simples, es tan fácil como comparar todos los elementos de una lista contra todos, si se cumple que uno es mayor o menor a otro, entonces los intercambia de posición.

Veamos a continuación el algoritmo correspondiente, para ordenar una lista de menor a mayor, partiendo de que los datos a ordenar están en una lista de N elementos:

1. Comparamos el primer elemento con el segundo, el segundo con el tercero el tercero con el cuarto, etc. Cuando el resultado de una comparación sea “mayor que”, se intercambian los valores de los elementos comparados. Con esto conseguimos llevar el valor mayor a la posición n. 2. Repetimos el punto 1, ahora para los N-1 primeros elementos de la lista y así sucesivamente. 3. Repetimos el punto 1, ahora para los N-2 primeros elementos de la lista y así sucesivamente. 4. El proceso termina después de repetir el punto 1, N-1 veces, o cuando al finalizar la ejecución del punto 1 no haya habido ningún cambio.

Código en C del algoritmo: Nombre Tipo Uso lista Cualquiera Lista a ordenar TAM Constante entera Tamaño de la lista i Entero Contador j Entero Contador temp El mismo que los elementos de la lista Para realizar los intercambios

for (i=0; i<TAM; i++) { for j=0 ; j<TAM - 1; j++) { if (lista[j] > lista[j+1]) { temp = lista[j]; lista[j] = lista[j+1]; lista[j+1] = temp; } } }

Veamos con un ejemplo, cómo funciona el algoritmo: Supongamos el siguiente arreglo de enterosa a ordenarse en formato ascendente: int datos[6] = {40,21,4,9,10,35};

Primera pasada: {40, 21, 4, 9, 10, 35} {21, 40, 4, 9, 10, 35} <-- Se cambia el 21 por el 40. {21, 4, 40, 9, 10, 35} <-- Se cambia el 40 por el 4. {21, 4, 9, 40, 10, 35} <-- Se cambia el 9 por el 40. {21, 4, 9, 10, 40, 35} <-- Se cambia el 40 por el 10. {21, 4, 9, 10, 35, 40} <-- Se cambia el 35 por el 40.

Segunda pasada: {21, 4, 9, 10, 35, 40} {4, 21, 9, 10, 35, 40} <-- Se cambia el 21 por el 4. {4, 9, 21, 10, 35, 40} <-- Se cambia el 9 por el 21. {4, 9, 10, 21, 35, 40} <-- Se cambia el 21 por el 10. {4, 9, 10, 21, 35, 40} <-- No hay cambio entre el 21 y el 35. {4, 9, 10, 21, 35, 40} <-- El 35 y el 40, no se comparan, porque el 40 es el mayor.

En este ejemplo, los datos ya están ordenados, pero para comprobarlo habría que hacer una tercera, cuarta y quinta comprobación.

Ejemplo #1: Programa en C que permite introducir una lista con los pesos de los N estudiantes de la asignatura de Programación Estructurada e imprime los pesos en formato ascendente (menor a mayor).

//burbuja_ascendente.c #include<stdio.h> #include<stdlib.h> void main() { float *pesos,temp; int i,j,nest; printf("Cuantos estudiantes son?: "); scanf("%d",&nest); **pesos = (float ) malloc(nest sizeof(float)); if(pesos==NULL) { printf("Insuficiente Espacio de Memoria\n"); exit(-1); //Salir del Programa }

Ejemplo #2: Programa en C que permite introducir una lista con los pesos de los N estudiantes de la asignatura de Programación Estructurada e imprime los pesos en formato descendente (mayor a menor).

//burbuja_descendente.c #include<stdio.h> #include<stdlib.h> void main() { float *pesos,temp; int i,j,nest; printf("Cuantos estudiantes son?: "); scanf("%d",&nest); **pesos = (float ) malloc(nest sizeof(float)); if(pesos==NULL) { printf("Insuficiente Espacio de Memoria\n"); exit(-1); //Salir del Programa }

for (i = 0; i<nest; i++) { printf("Peso del Estudiante[%d]: ",i+1); scanf("%f",(pesos+i)); } printf("\n****ARRAY ORIGINAL****\n"); for (i = 0;i<nest;i++) printf("Peso[%d]: %.1f\n",i+1,*(pesos+i));

/Ordenación del array con el método de la burbuja en formato descendente/ for (i=0;i<nest;i++) for(j=0;j<(nest-1);j++) if(pesos[j] < pesos[j+1]) { temp=pesos[j]; pesos[j] = pesos[j+1]; pesos[j+1] = temp; }**

printf("\n****ARRAY ORDENADO EN FORMATO DESCENDENTE****\n"); for (i = 0;i<nest;i++) printf("Peso[%d]: %.1f\n",i+1,*(pesos+i)); } Ejemplo de Salida:

1.5.1 ANÁLISIS DEL ALGORITMO DE LA BURBUJA:

Éste es el análisis para la versión no optimizada del algoritmo:

  • Estabilidad: Este algoritmo nunca intercambia registros con claves iguales. Por lo tanto es estable.
  • Requerimientos de Memoria: Este algoritmo sólo requiere de una variable adicional para realizar los intercambios.
  • Tiempo de Ejecución: El ciclo interno se ejecuta n veces para una lista de n elementos. El ciclo externo también se ejecuta n veces. Es decir, la complejidad es n
    • n = O(n^2 ). El comportamiento del caso promedio depende del orden de entrada de los datos, pero es sólo un poco mejor que el del peor caso, y sigue siendo O(n^2 ).

Ventajas:

  1. Fácil implementación.
  2. No requiere memoria adicional. Desventajas:
  3. Muy lento.
  4. Realiza numerosas comparaciones.
  5. Realiza numerosos intercambios.

Este algoritmo es uno de los más pobres en rendimiento.

Veamos con un ejemplo, cómo funciona el algoritmo: Supongamos el siguiente arreglo de enterosa a ordenarse en formato ascendente: int datos[5] = {4,3,5,2,1};

4 - 3 - 5 - 2 - 1 temp toma el valor del segundo elemento, 3. La primera carta es el 4. Ahora comparamos: 3 es menor que 4. Luego desplazamos el 4 una posición a la derecha y después copiamos el 3 en su lugar. 4 - 4 - 5 - 2 - 1 3 - 4 - 5 - 2 - 1 El siguiente elemento es 5. Comparamos con 4. Es mayor que 4, así que no ocurren intercambios. Continuamos con el 2. Es menor que cinco: desplazamos el 5 una posición a la derecha: 3 - 4 - 5 - 5 - 1 Comparamos con 4: es menor, así que desplazamos el 4 una posición a la derecha: 3 - 4 - 4 - 5 - 1 Comparamos con 3. Desplazamos el 3 una posición a la derecha: 3 - 3 - 4 - 5 - 1 Finalmente copiamos el 2 en su posición final: 2 - 3 - 4 - 5 - 1 El último elemento a ordenar es el 1. Cinco es menor que 1, así que lo desplazamos una posición a la derecha: 2 - 3 - 4 - 5 - 5 Continuando con el procedimiento la lista va quedando así: 2 - 3 - 4 - 4 - 5 2 - 3 - 3 - 4 - 5 2 - 2 - 3 - 4 - 5 1 - 2 - 3 - 4 - 5

Ejemplo #3: Programa en C que permite introducir una lista con los pesos de los N estudiantes de la asignatura de Programación Estructurada e imprime los pesos ordenados de menor a mayor utilizando el método de inserción.

//inserción.c #include<stdio.h> #include<stdlib.h> void main() { float *pesos,indice; int i,j,nest,e; printf("Cuantos estudiantes son?: "); scanf("%d",&nest); **pesos = (float ) malloc(nest sizeof(float));

if(pesos==NULL) { printf("Insuficiente Espacio de Memoria\n"); exit(-1); //Salir del Programa } for (i=0; i<nest; i++) { printf("Peso del Estudiante[%d]: ",i+1); scanf("%f",&pesos[i]); }

printf("\n****ARRAY ORIGINAL****\n"); for (i=0;i<nest;i++) printf("Peso[%d]: %.1f\n",i+1,pesos[i]);

printf("\n****ARRAY ORDENADO****\n"); for (i = 0;i<nest;i++) printf("Peso[%d]: %.1f\n",i+1,pesos[i]);

printf("\n****ARRAY ORDENADO****\n"); for (i=0;i<nest;i++) printf("Peso[%d]: %.1f\n",i+1,pesos[i]); }

//Ordenación del array a través del método de inserción for (e=1;e<nest;e++) { indice = pesos[e]; j = e-1; while (j>=0 && pesos[j]>indice) { pesos[j+1] = pesos[j]; j--; } pesos[j+1]=indice; }

printf("\n****ARRAY ORIGINAL****\n"); for (i=0;i<nest;i++) printf("Peso[%d]: %.1f\n",i+1,pesos[i]);

printf("\n****ARRAY ORDENADO****\n"); for (i = 0;i<nest;i++) printf("Peso[%d]: %.1f\n",i+1,pesos[i]); }

Ejemplo de Salida:

//Ordenación del array a través del método de inserción for (e=1;e<nest;e++) { indice = pesos[e]; j = e-1; while (j>=0 && pesos[j]<indice) { pesos[j+1] = pesos[j]; j--; } pesos[j+1]=indice; }

1.6.1 ANÁLISIS DEL ALGORITMO:

  • Estabilidad: Este algoritmo nunca intercambia registros con claves iguales. Por lo tanto es estable.
  • Requerimientos de Memoria: Una variable adicional para realizar los intercambios.
  • Tiempo de Ejecución: Para una lista de n elementos el ciclo externo se ejecuta n- veces. El ciclo interno se ejecuta como máximo una vez en la primera iteración, 2 veces en la segunda, 3 veces en la tercera, etc. Esto produce una complejidad O(n^2 ).

Ventajas: 1 Fácil implementación. 2 Requerimientos mínimos de memoria.

Desventajas: 1 Lento. 2 Realiza numerosas comparaciones.

Este también es un algoritmo lento, pero puede ser de utilidad para listas que están ordenadas o semiordenadas, porque en ese caso realiza muy pocos desplazamientos.

1.7 ORDENAMIENTO POR EL MÉTODO DE QUICKSORT:

El ordenamiento rápido (quicksort en inglés) es un algoritmo basado en la técnica de divide y vencerás. Esta es probablemente la técnica más rápida conocida. Fue desarrollada por Tony Hoare en 1960.

La idea del algoritmo es simple, se basa en la división en particiones de la lista a ordenar, por lo que se puede considerar que aplica la técnica divide y vencerás. El método es, posiblemente, el más pequeño de código, más rápido, más elegante, más interesante y eficiente de los algoritmos de ordenación conocidos.

El método se basa en dividir los n elementos de la lista a ordenar en dos partes o particiones separadas por un elemento: una partición izquierda, un elemento central denominado pivote o elemento de partición, y una partición derecha. La partición o división se hace de tal forma que todos los elementos de la primera sublista (partición izquierda) son menores que todos los elementos de la segunda sublista (partición derecha). Las dos sublistas se ordenan entonces independientemente.

Para dividir la lista en particiones (sublistas) se elige uno de los elementos de la lista y se utiliza como pivote o elemento de partición. Si se elige una lista cualquiera con los elementos en orden aleatorio, se puede seleccionar cualquier elemento de la lista como pivote, por

Nombre Procedimiento: OrdRap Parámetros: lista a ordenar (lista) índice inferior (inf) índice superior (sup)

// Inicialización de variables

  1. elem_div = lista[sup];
  2. i = inf - 1;
  3. j = sup;
  4. cont = 1; // Verificamos que no se crucen los límites
  5. if (inf >= sup)
    1. retornar; // Clasificamos la sublista
  6. while (cont)
  7. while (lista[++i] < elem_div);
  8. while (lista[--j] > elem_div);
    1. if (i < j)
    2. temp = lista[i];
    3. lista[i] = lista[j];
    4. lista[j] = temp;
    5. else
    6. cont = 0; // Copiamos el elemento de división en su posición final
    7. temp = lista[i];
    8. lista[i] = lista[sup];
    9. lista[sup] = temp; // Aplicamos el procedimiento recursivamente a cada sublista
  9. OrdRap (lista, inf, i - 1);
  10. OrdRap (lista, i + 1, sup);

1.7.1 LOS PASOS QUE SIGUE EL ALGORITMO QUICKSORT:

  1. Seleccionar el elemento central de a[0:n-1] como pivote.
  2. Dividir los elementos restantes en particiones izquierda y derecha, de modo que ningún elemento de la izquierda tenga una clave (valor) mayor que el pivote y que ningún elemento a la derecha tenga una clave más pequeña que la del pivote.
  3. Ordenar la partición izquierda utilizando quicksort recursivamente.
  4. Ordenar la partición derecha utilizando quicksort recursivamente.
  5. La solución es partición izquierda seguida por el pivote y a continuación partición derecha.

Veamos con un ejemplo, cómo funciona el algoritmo: Supongamos el siguiente arreglo de enterosa a ordenarse en formato ascendente: int datos[7] = {5,3,7,6,2,1,4};

5 - 3 - 7 - 6 - 2 - 1 - 4 Comenzamos con la lista completa. El elemento divisor será el 4: 5 - 3 - 7 - 6 - 2 - 1 - 4 Comparamos con el 5 por la izquierda y el 1 por la derecha. 5 - 3 - 7 - 6 - 2 - 1 – 4 5 es mayor que cuatro y 1 es menor. Intercambiamos: 1 - 3 - 7 - 6 - 2 - 5 - 4 Avanzamos por la izquierda y la derecha: 1 - 3 - 7 - 6 - 2 - 5 - 4 3 es menor que 4: avanzamos por la izquierda. 2 es menor que 4: nos mantenemos ahí. 1 - 3 - 7 - 6 - 2 - 5 - 4 7 es mayor que 4 y 2 es menor: intercambiamos. 1 - 3 - 2 - 6 - 7 - 5 - 4 Avanzamos por ambos lados: 1 - 3 - 2 - 6 - 7 - 5 - 4 En este momento termina el ciclo principal, porque los índices se cruzaron. Ahora intercambiamos lista[i] con lista[sup] (pasos 16-18): 1 - 3 - 2 - 4 - 7 - 5 - 6 Aplicamos recursivamente a la sublista de la izquierda (índices 0 - 2). Tenemos lo siguiente: 1 - 3 - 2 1 es menor que 2: avanzamos por la izquierda. 3 es mayor: avanzamos por la derecha. Como se intercambiaron los índices termina el ciclo. Se intercambia lista[i] con lista[sup]: 1 - 2 - 3

Al llamar recursivamente para cada nueva sublista (lista[0]-lista[0] y lista[2]-lista[2]) se retorna sin hacer cambios (condición 5.).Para resumir te muestro cómo va quedando la lista:

Segunda sublista: lista[4]-lista[6] 7 - 5 - 6 5 - 7 - 6 5 - 6 - 7 Para cada nueva sublista se retorna sin hacer cambios (se cruzan los índices).

Finalmente, al retornar de la primera llamada se tiene el arreglo ordenado: 1 - 2 - 3 - 4 - 5 - 6 - 7

Ejemplo #5: Programa en C que inicializa una lista con valores e imprime los dichos datos ordenados de menor a mayor utilizando el método de quicksort. Nota: Versión Array.

/**********Ordencion Quicksort en formato Ascendente. Método recursivo****/ #include<stdio.h> #include<stdlib.h> void quicksort(int [], int, int); void main() { int lista[] = {9,4,2,7,5},i,n_elementos; n_elementos = sizeof(lista)/sizeof(int); printf ("******ARRAY ORIGINAL******\n--------------------------\n"); for (i = 0; i < n_elementos; i++) printf("%d ",lista[i]); quicksort(lista,0,n_elementos-1); printf ("\n\nARRAY ORDENADO EN FORMATO ASCENDENTE: \n"); printf ("--------------------------------------\n"); for (i = 0; i < n_elementos; i++) printf("%d ",lista[i]); }

//Función Quicksort en formato ascendente void quicksort(int lista[], int inf, int sup) { int mitad, x,izq, der; izq = inf; der = sup; mitad = lista[(izq + der)/2]; do { while(lista[izq] < mitad && izq < sup) izq++; while(mitad < lista[der] && der > inf) der--; if(izq <= der) { x = lista[izq], lista[izq] = lista[der], lista[der] = x; izq++; der--; }

}while(izq <= der);

if(inf<der) quicksort(lista, inf,der); //mismo proceso con sublista izqda if(izq<sup) quicksort(lista,izq,sup); //mismo proceso con sublista derecha }

Ejemplo de Salida:

Ejemplo #6: Programa en C que inicializa una lista con valores e imprime los dichos datos ordenados de mayor a menor utilizando el método de quicksort. Nota: Versión Array.

/************* Ordencion Quicksort Descendente. Método recursivo***************/ #include<stdio.h> #include<stdlib.h> void quicksort(int [], int, int); void main() { int lista[] = {9,4,2,7,5},i,n_elementos; n_elementos = sizeof(lista)/sizeof(int);

printf ("******ARRAY ORIGINAL******\n--------------------------\n"); for (i = 0; i < n_elementos; i++) printf("%d ",lista[i]);

quicksort(lista,0,n_elementos-1);

printf ("\n\nARRAY ORDENADO EN FORMATO ASCENDENTE: \n"); printf ("--------------------------------------\n"); for (i = 0; i < n_elementos; i++) printf("%d ",lista[i]); }