62040b4ce769f
62040b4ce769f
62040b4ce769f
Ordenación,
búsqueda e intercalación
Nombre del Profesor: Adrián Rivero Jiménez
1
Bloque II. Ordenación, búsqueda e intercalación
2.1 Ordenación
Hoy día gran parte del tiempo de procesamiento se ocupa en ordenar los datos, lo anterior
debido a que prácticamente todo lo que utilizamos requiere un orden específico, piensa por
ejemplo en tu playlist favorita, ¿de cuantas maneras puedes ordenarla?; o los mails en tu
correo electrónico, ¿Cómo los ordenas cuando los lees? y como olvidar tus fotos en el celular
donde están ordenas de la más reciente a la más antigua.
En esta unidad veremos la ordenación enfocada en arrays (arreglos) ya que esta también
puede suceder en archivos; la principal diferencia entre ambas es la capacidad de
almacenamiento ya que un archivo siempre podrá contener más información que un array;
sin embargo el precio de esa mayor capacidad de almacenamiento es un menor rendimiento
frente al que ofrece el array el cual tiene una capacidad de almacenamiento mucho menor.
Las operaciones que se realiza con arrays ocurren en memoria interna y de ahí su alta
velocidad; por otro lado, para el caso de los archivos la ordenación ocurre en los soportes en
que la información está almacenada, por ejemplo discos duros.
Cabe destacar que hay diversos métodos de ordenamiento y el que se elija deberá basarse
en el tamaño del arreglo, el tipo de dato y la memoria con la que cuenta nuestro equipo.
Analizando lo anterior veremos que en un array de tamaño n, para cada elemento quede en
la posición correcta debemos ejecutar (n - 1) comparaciones y derivado de ellas podrán ocurrir
(n - 1) intercambios ya que el resultado de las comparaciones es binario, simplemente
determinamos si se cambia o no de posición el elemento; dicho lo anterior tenemos que el
total de operaciones a realizar para ordenar el array es:
(n -1) * (n - 1) = (n - 1)2
Partamos de tener un array cuyo tamaño es de 5 elementos numéricos, los elementos que
viven dentro del array son 45, 6, 23, 12, 1; la salida que se espera es un array ordenado 1, 6,
12, 23 y 45 teniendo como primer elemento al valor numérico más pequeño y como último al
valor numérico más grande.
2
Entonces sabiendo que nuestro array tiene un n = 5 sustituimos en (n-1)2 y obtenemos (5-1)2
lo que nos da 42 que al final es 16, esto quiere decir que para ordenar el array debemos hacer
16 comparaciones; dichas comparaciones se expresan en la siguiente tabla:
Pongamos otro ejemplo, veamos ahora un array de tamaño 5 pero con la particularidad de
que está casi ordenado, si aplicamos el método veremos que tenemos que realizar las
mismas 16 comparaciones, te invito a que saques tus propias conclusiones.
#include <iostream>
#include <stdlib.h>
int main()
{
int contador = 0;
int contadorComparaciones = 0;
3
int tamanioArray = 5;
int edades[tamanioArray];
int varAux = 0;
std::string continuar;
system("cls");
do
{
std::cout << "Teclea la edad del alumno " + std::to_string((contador+1)) + ": ";
std::cin >> edades[contador];
contador ++;
} while(contador < tamanioArray);
Para lograr lo anterior debemos auxiliarnos de una variable que contenga el valor a ordenar,
además siempre comenzaremos por la segunda posición del array; los pasos para hallar la
posición correcta serán los siguientes:
4
Comparar nuestra variable con el valor que exista en la posición anterior, si este es mayor
entonces lo movemos hacia adelante y seguimos comparando los valores hacia atrás, si el
valor es menor o hemos llegado al inicio del arreglo saldremos y colocaremos en la posición
indicada el valor de nuestra variable, debemos repetir esto hasta terminar de ordenar el array.
La siguiente tabla nos muestra el comportamiento del algoritmo de ordenación para un array
inicial de 45, 6, 23, 12 y 1. En este ejemplo podemos ver que respecto al método de burbuja
las operaciones en volumen se han reducido considerablemente.
#include <iostream>
#include <stdlib.h>
int main()
{
int contador = 0;
int valorOrdenar = 0;
int contadorComparaciones = 0;
int tamanioArray = 5;
int *edades = new int[tamanioArray];
int varAux = 0;
std::string continuar;
system("cls");
do
{
std::cout << "Teclea la edad del alumno " + std::to_string((contador+1)) + ": ";
std::cin >> edades[contador];
contador ++;
5
} while(contador < tamanioArray);
6
Si bien este algoritmo es más eficiente que los anteriores, también es cierto que el codificarlos
es más complejo como vemos en el siguiente ejemplo:
#include <iostream>
#include <stdlib.h>
int main()
{
int contador = 0;
int valorMenor = 0;
int posicionValorMenor = 0;
int tamanioArray = 5;
int *edades = new int[tamanioArray];
int valorIntercambio = 0;
std::string continuar;
system("cls");
do
{
std::cout << "Teclea la edad del alumno " + std::to_string((contador+1)) + ": ";
std::cin >> edades[contador];
contador ++;
} while(contador < tamanioArray);
delete[] edades;
}
7
2.5 Método de Shell
En el caso del método de inserción se deben hacer demasiadas comparaciones antes de
llegar al resultado, sin embargo para hacer este algoritmo más eficiente Donald Shell agregó
la idea de comparar los valores de manera salteada a fin de minimizar la cantidad de
comparaciones, estos saltos tendrán una longitud fija y mayor a una posición.
El primer paso es determinar la longitud de dichos saltos, para ello tomaremos el número de
elementos en el array y lo dividiremos entre 2 de tal manera que la distancia de los saltos
será N/2, al finalizar el recorrido del array recalculamos el salto y dividimos la longitud entre
2 nuevamente, esto se hará de manera cíclica mientras que el tamaño del salto sea mayor a
cero pues eso impediría recorrer el array. Veamos cómo quedaría codificado en C++.
#include <iostream>
#include <stdlib.h>
int main()
{
int contador = 0;
int valorMenor = 0;
int posicionValorMenor = 0;
int tamanioArray = 5;
int *edades = new int[tamanioArray];
int valorIntercambio = 0;
std::string continuar;
system("cls");
do
{
std::cout << "Teclea la edad del alumno " + std::to_string((contador+1)) + ": ";
std::cin >> edades[contador];
contador ++;
} while(contador < tamanioArray);
8
{
std::cout << std::to_string(k+1) + ". "+ std::to_string(edades[k]) + "\n";
}
std::cin >> continuar;
delete[] edades;
}
Una vez que los arrays fueron creados debemos comenzar a ordenarlos, a continuación se
muestra la codificación del algoritmo utilizando recursividad.
#include <iostream>
using namespace std;
// Swap two elements - Utility function
void swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
//quicksort algorithm
void quickSort(int arr[], int low, int high)
{
if (low < high)
{
9
//partition the array
int pivot = partition(arr, low, high);
int main()
{
int arr[] = {19,83,74,1,35,99,87,45,21,59,66};
int n = sizeof(arr)/sizeof(arr[0]);
cout<<"Input array"<<endl;
displayArray(arr,n);
cout<<endl;
quickSort(arr, 0, n-1);
cout<<"Array sorted with quick sort"<<endl;
displayArray(arr,n);
return 0;
}
2.7 Búsqueda
Las búsquedas en una parte fundamental dentro del manejo de la información, esto debido a
que no importa si es una canción, un archivo, una palabra o un video, todo requiere ser
localizado dentro de esos grandes volúmenes de información. Incluso una de las tareas
dentro de las búsquedas es descartar que el elemento exista, es decir, no solo es buscar y
encontrar sino en algunos casos buscar y no encontrar.
Los algoritmos de búsqueda más habituales son el secuencial, el binario y el que realiza la
transformación de claves (hash).
Como se puede apreciar esto no es eficiente para arrays medianos o grandes ya que el
elemento a buscar puede estar en cualquier lugar o como ya se mencionó puede no estarlo,
10
imagina que aplicaras a una beca junto con otros 2000 alumnos y publicaran los resultados
con los nombres en desorden, ¿te imaginas como tendrías que buscar tu nombre en esa
lista?, seguro de manera secuencial y esto te da una idea de cuan ineficiente es esto para
búsquedas de volumen considerable.
Ahora bien, piensa en cuando organizas una reunión en casa y haces tú lista de lo que te
falta por comprar, esa lista es pequeña y buscar en ella de manera secuencial es rápido, no
importa el orden de las cosas pues al tener pocos elementos la recorres rápido.
Como puedes ver, el uso de la búsqueda secuencial está en función del tamaño de la lista,
no hay mejor fórmula para determinar cuándo usar estas búsquedas que el sentido común. A
continuación una implementación de un algoritmo de búsqueda secuencial.
#include <iostream>
#include <stdlib.h>
#include <time.h>
int main()
{
int contador = 0;
int edadBusqueda = 0;
int tamanioArray = 50;
int *edades = new int[tamanioArray];
bool existe = false;
std::string continuar;
system("cls");
srand (time(NULL));
std::cout << "Llenando vector " << std::endl;
std::cout << std::endl << "Los valores del arreglo son: " << std::endl;
std::cout << std::endl << "Teclea la edad del alumno a buscar: ";
std::cin >> edadBusqueda;
for(contador = 0; contador < tamanioArray; contador ++)
{
if (edades[contador] == edadBusqueda)
{
std::cout << "El valor " + std::to_string(edadBusqueda) + " fue hallado en la
posición " + std::to_string(contador) << std::endl;
existe = true;
}
}
11
if (existe == false)
{
std::cout << "El valor " << std::to_string(edadBusqueda) << " no existe en el
arreglo" << std::endl;
}
delete[] edades;
}
En caso de que dicho elemento central no corresponda al valor que buscamos, entonces
validemos si éste valor central es mayor o menor que el elemento que se está buscando, si
es menor iremos a buscar en la mitad de la izquierda descartando todos los valores desde el
elemento central hasta el elemento final, es decir, el lado derecho ya que al estar ordenado
es seguro que nuestro valor no está en ese lado, si el valor es mayor al central haremos el
mismo descarte pero con los elementos de la izquierda, o sea, desde el primer elemento del
array hasta el elemento central.
Debemos repetir esa división del vector hasta que hallemos el valor deseado o en su defecto
comprobemos que el valor no existe en el arreglo.
12
#include <iostream>
#include <stdlib.h>
int main()
{
int contador = 0;
int edadBusqueda = 0;
int LimInferior = 0;
int LimSuperior = 24;
int edades[] = {0,1,3,5,7,9,13,15,17,19,21,23,25,27,31,33,35,37,39,41,43,45,47,49};
int comparaciones = 0;
std::string continuar;
int mitadArray = 0;
mitadArray = LimSuperior / 2;
system("cls");
std::cout << std::endl << "Teclea la edad del alumno a buscar: ";
std::cin >> edadBusqueda;
13
LimSuperior = mitadArray - 1;
}
else
{
LimInferior = mitadArray + 1;
}
mitadArray = (LimInferior + LimSuperior)/2;
comparaciones ++;
}
if(edades[mitadArray] == edadBusqueda)
{
std::cout << "El valor " << std::to_string(edadBusqueda) << " existe y se hicieron
" << std::to_string(comparaciones) << std::endl;
}
else
{
std::cout << "El valor " << std::to_string(edadBusqueda) << " no existe" <<
std::endl;
}
14
Reasignación: Consiste en que si ese elemento comparte misma clave con otro, se deberá
correr una posición siguiente dentro del vector y este se encuentre desocupado, caso
contrario, continuara hasta que encuentre uno disponible.
Funciones Hash.
Restas Sucesivas: Esta función se emplea con claves numéricas entre las que existen huecos
de tamaño conocido, obteniéndose direcciones consecutivas. Esta función lo que hace es
aplicar una resta de manera sucesiva en donde n es la clave y size es el tamaño del vector,
es una función recursiva donde su caso base será cuando n valga cero, o sea menor que el
tamaño del vector, para así obtener una clave valida.
– H (K)= K MOD N
Para tener una mejor uniformidad en la distribución, N debe ser un número primo o divisible
por muy pocos números. Recordemos que n es la clave, y size el tamaño del arreglo.
15
Suma de dígitos: La implementación de “suma de dígitos”, lo que haces es ir sumando los
dígitos de la clave como por ejemplo un numero de cliente y lo consecuente a eso será la
clave/posición de ese elemento dentro del vector.
N es el campo clave, size el tamaño del arreglo. Como la anterior también es una función
recursiva con mismo caso base, pero el caso recursivo es distinto.
2.11 Intercalación
Se refiere a obtener un nuevo array producto de combinar dos diferentes arrays previamente
ordenados, imaginemos que tenemos un primer array de tamaño cinco con los elementos
0,2,4,6 y 8 y lo combinamos con un segundo array de tamaño seis, el cual contiene 1,3,5,7,9
y 10; el array esperado sería uno de tamaño once debido a que debe ser del tamaño de la
suma de los dos primeros arrays, este nuevo array contendría los elementos ordenados
0,1,2,3,4,5,6,7,8,9 y 10.
Debido a que ambos arrays están ordenados, la solución es muy sencilla; iremos recorriendo
en orden los arrays y compararemos uno a uno sus elementos, cuando un elemento sea
menor al otro este se agregará al nuevo vector y avanzaremos el índice de ese array l
siguiente elemento y continuaremos las comparaciones de manera sucesiva hasta terminar
de crear el nuevo array.
#include <iostream>
#include <stdlib.h>
int main()
{
int i = 0;
16
int j = 0;
int k = 0;
int tamanioArray1 = 5;
int tamanioArray2 = 8;
int tamanioArray3 = 13;
int array1[] = {0,2,4,6,8};
int array2[] = {1,3,5,7,9,10,11,12};
int *nuevo = new int[tamanioArray3];
std::string continuar;
system("cls");
17
Referencias
Joyanes Luis, (2008). Introducción a la programación. Fundamentos de Programación.
Algoritmos, Estructura De Datos y Objetos. (4a ed.). España. McGRAW-
HILL/INTERAMERICANA DE ESPAÑA.
Deitel & Deitel, (1995). Cómo programar en C/C++. (2a ed.). México. Prentice Hall
Hispanoamericana.
(2006), ¿Qué es y para qué sirve una tabla de decisión? Concepto y utilidad para resolver
problemas. Recuperado el 29 de octubre de 2021 de
https://www.aprenderaprogramar.com/index.php?option=com_content&view=article&id
=153:ique-es-y-para-que-sirve-una-tabla-de-decision-concepto-y-utilidad-para-
resolver-problemas-cu00112a&catid=28&Itemid=59
18
(04-Marzo-2016), Programación Lógica Y Funcional. Recuperado el 29 de octubre de 2021
de http://programacionlogyfun.blogspot.com/2016/03/programacion-logica-y-funcional-
la.html
19