Está en la página 1de 20

Lección 2.

1: Arreglos
Ha obtenido 0 punto(s) sobre 0 hasta ahora.

Arreglos
Un arreglo es un conjunto de datos o una estructura de datos homogéneos que se
encuentran ubicados en forma consecutiva en la memoria RAM (sirve para almacenar
datos en forma temporal).

Arreglos

Concepto
Grupo o colección finita, homogénea y ordenada de elementos.
:
Cuando hablamos de Estructuras de Datos hacemos referencia a un conjunto de datos
que poseen el mismo nombre, que pueden ser caracterizados por su organización y por
las operaciones que se definen en ella.
Datos simples
      Estándar --> Entero – Real – Caracteres - Lógico
Estructura de datos
    Estática --> Vectores y Matrices, Registro, Archivos
    Dinámica --> Lineales: Listas, Pilas Colas
                   --> No lineales: Arboles y Grafos
Las estructuras de datos estáticas son aquellas en las que el tamaño ocupado en
memoria se define antes que el programa se ejecute y el mismo no puede ser modificado
durante la ejecución.
Los tipos de datos que vimos hasta ahora son datos simples cuya característica común es
que cada variable representa a un elemento; en cambio los tipos de datos estructurados
tienen como particularidad que con un nombre o identificador se puede representar
múltiples datos individuales y a su vez cada uno de estos puede ser
referenciado independientemente.
Concepto
Arreglos se define como una colección finita, homogénea y ordenada de elementos.
Finita: Todo arreglo tiene un límite, es decir, debe determinarse cual será el número
máximo de elementos que podrán formar parte del arreglo.
Homogénea: Todos los elementos de un arreglo son del mismo tipo o naturaleza (todos
enteros, todos booleanos, etc.- ), pero nunca una combinación de distintos tipos.
Ordenada: Se debe determinar cual es el primer elemento, el segundo, el tercero..... y el
enésimo elemento.
Características
Si un arreglo tiene la característica de que puede almacenar a N elementos del mismo tipo,
deberá tener la posibilidad de permitir seleccionar a cada uno de ellos. Así se distinguen
dos partes en los arreglos.
● Los componentes o elementos (valores que se almacenan en c/u de las casillas)
● Los índices (Permiten hacer referencia a los componentes)
El número total de componentes (NTC) es igual al límite superior (LS) menos límite inferior
(LI) mas 1
NTC = LS - LI + 1
El tipo de índice puede ser cualquier tipo ordinal (carácter, entero, enumerado)
● El tipo de los componentes puede ser cualquiera (entero, real, cadena de caracteres,
registro, etc.)
● Se utilizan ( ) para indicar el índice de un arreglo. Entre los ( ) se debe
escribir un valor ordinal (puede ser una variable, una constante o una expresión que dé
como resultado un valor ordinal)
Ejemplo
Sea V un arreglo de 30 elementos enteros con índices enteros.
V = (52,12,31,102,....75)
V (50)= v(1), v(2), v(3),...., v(30),
Su representación gráfica serà

Donde
NTC = (30 – 1 + 1) = 30
Cada componente del arreglo V será un número entero, y podrá
accederse por medio de un índice que será un valor comprendido entre 1 y 30.
De esta manera, por ejemplo:
V(1) hace referencia al elemento de la posición 1 cuyo valor es 52
V(2) hace referencia al elemento de la posición 2 cuyo valor es 12
En cuanto a las dimensiones los arreglos pueden ser:
● Unidimensional o vector: un solo índice
● Bidimensional o matriz: dos índices
● Multidimensional: mas de dos índices
Diferencia con registros
Las dos diferencias sustanciales entre arreglos y registro son:
1) Un arreglo puede almacenar N elementos del mismo tipo, mientras que un registro
puede almacenar N elementos de distintos tipos que se llaman campos.
2) Los componentes de un arreglo se acceden por medio de índices, mientras que en un
registro los campos se acceden por medio de su nombre, el cual es único.
Arreglos Unidimensionales: Vectores
Operaciones : Podemos clasificar a las operaciones en las que intervienen arreglos de la
siguiente manera:
● Lectura / escritura
● Recorrido
● Asignación
● Actualización (Añadir, eliminar, insertar)
● Ordenación
● Búsqueda
Como los arreglos son datos estructurados, muchas de estas operaciones no pueden
llevarse a cabo de manera global, sino que se debe trabajar sobre cada componente.
Seguidamente se analizará cada una de estas operaciones, excepto las dos últimas
(ordenación y búsqueda) que serán estudiadas en capítulos posteriores.
Operaciones: Lectura / escritura
El proceso de lectura /escritura de un arreglo se realiza de la siguiente manera:
Leer V(n)      Lee todo el arreglo
Escribir V(n) Escribe todo el arreglo
Leer V(3)     Lee el elemento 3 del arreglo
Operaciones: Recorrido
Recorrer un vector significa acceder a todos y a cada uno de sus elementos desde el
principio hasta el final o viceversa (Recorrido Secuencial).
Se puede acceder a los elementos de un vector para introducir datos (leer) en él o bien
para ver su contenido (escribir). A la operación de acceder a todos los elementos para
efectuar una acción determinada se denomina recorrido del vector. Esta operación se
realiza usando estructuras repetitivas, cuya variable de control I, se utiliza como subíndice
del vector (por ejemplo V(n). El incremento del contador del bucle producirá el tratamiento
sucesivo de los elementos del vector.
Esta operación es muy utilizada en este tipo de estructuras de datos, dado que cuando se
está en presencia de un vector, el acceso a toda la información se realiza recorriendolo.
En algunos casos (Recorrido Randomico) se puede acceder a un determinado elemento
o a varios de ellos con ciertas características sin necesidad de recorrer todo el arreglo, por
ejemplo acceder solo al último elemento que sabemos a priori posee la suma de los
elementos anteriores.
Operaciones: Actualización
Muchas veces resulta interesante que dado un arreglo, puedan añadirse nuevos
elementos o eliminar o insertar componentes. Estas resultan las tres operaciones
elementales que se pueden realizar en un arreglo: añadir,
eliminar e insertar elementos.
Cuando se realiza una operación de añadir un nuevo elemento a continuación del último
valor no nulo, la única condición necesaria para esta operación es comprobar que haya
espacio para el nuevo elemento.
La operación de eliminar un elemento al final del arreglo no presenta ningún problema; en
cambio, si el borrado se realiza en el interior del mismo esto provoca el efecto contrario al
de insertar, el movimiento deberá ser hacia
arriba (I-1) de los elementos inferiores a él para reorganizar el vector.

Ordenaciones en Arreglos

La importancia de mantener nuestros arreglos ordenados radica en que es mucho más


rápido tener acceso a un dato en un arreglo ordenado que en uno
desordenado. Existen muchos algoritmos para la ordenación de elementos en
arreglos, algunos de ellos son:

Este método consiste en seleccionar el elemento más pequeño de nuestra lista para
colocarlo al inicio y así excluirlo de la lista. Para ahorrar espacio, siempre que vayamos a
colocar un elemento en su posición correcta lo intercambiaremos por aquel que la esté
ocupando en ese momento.

Ordenación por burbuja


Es el método de ordenación más utilizado por su fácil comprensión y programación,
pero es importante señalar que es el más ineficiente de todos los métodos. Este
método consiste en llevar los elementos menores a la izquierda del arreglo ó los
mayores a la derecha del mismo. La idea básica del algoritmo es comparar pares de
elementos adyacentes e intercambiarlos entre sí hasta que todos se encuentren
ordenados.
Ordenación por mezcla
Este algoritmo consiste en partir el arreglo por la mitad, ordenar la mitad izquierda,
ordenar la mitad derecha y mezclar las dos mitades ordenadas en un array ordenado.
Este último paso consiste en ir comparando pares sucesivos de elementos (uno de
cada mitad) y poniendo el valor más pequeño en el siguiente hueco.
Algoritmos de búsqueda que existen

 Búsquedas en Arreglos: Una búsqueda es el proceso mediante el cual podemos


localizar un elemento con un valor específico dentro de un conjunto de datos.
Terminamos con éxito la búsqueda cuando el elemento es encontrado.
 Búsqueda secuencial: A este método también se le conoce como búsqueda lineal
y consiste en empezar al inicio del conjunto de elementos, e ir a través de ellos hasta
encontrar el elemento indicado ó hasta llegar al final de arreglo. Este es el método de
búsqueda más lento, pero si nuestro arreglo se encuentra completamente
desordenado es el único que nos podrá ayudar a encontrar el dato que buscamos.
 Búsqueda binaria: Las condiciones que debe cumplir el arreglo para poder usar
búsqueda binaria son que el arreglo este ordenado y que se conozca el número de
elementos. Este método consiste en lo siguiente: comparar el elemento buscado con el
elemento situado en la mitad del arreglo, si tenemos suerte y los dos valores
coinciden, en ese momento la búsqueda termina. Pero como existe un alto porcentaje
de que esto no ocurra, repetiremos los pasos anteriores en la mitad inferior del arreglo
si el elemento que buscamos resulto menor que el de la mitad del arreglo, o en la
mitad superior si el elemento buscado fue mayor. La búsqueda termina cuando
encontramos el elemento o cuando el tamaño del arreglo a examinar sea cero.
 Búsqueda por hash: La idea principal de este método consiste en aplicar una
función que traduce el valor del elemento buscado en un rango de direcciones
relativas. Una desventaja importante de este método es que puede ocasionar
colisiones.

Notación
La representación de un elemento en un vector se suele hacer mediante
el identificador del vector seguido del índice entre corchetes, paréntesis o llaves:
Notación Ejemplos
vector[índice_1,índice_2...,índice_N] (Java, Lexico, Perl, etc.)
vector[índice_0][índice_1]...[índice_N] (C, C++, PHP, etc.)
vector(índice_1,índice_2...,índice_N) (Basic)
Aunque muchas veces en pseudocódigo y en libros de matemática se representan como
letras acompañadas de un subíndice numérico que indica la posición a la que se quiere
acceder. Por ejemplo, para un vector "A":

 (vector unidimensional)
Forma de acceso
La forma de acceder a los elementos de la matriz es directa; esto significa (Acceso
Randomico) que el elemento deseado es obtenido a partir de su índice y no hay que ir
buscándolo elemento por elemento (en contraposición, en el caso de una lista, para llegar,
por ejemplo, al tercer elemento hay que acceder a los dos anteriores o almacenar un
apuntador o puntero que permita acceder de manera rápida a ese elemento).
Para trabajar con vectores muchas veces es preciso recorrerlos. Esto se realiza por medio
de bucles. El siguiente pseudocódigo muestra un algoritmo típico para recorrer un vector y
aplicar una función ' ' a cada una de las componentes del vector:

i = 0

mientras (i < longitud)

//Se realiza alguna operación con el vector en la i-ésima posición

f(v[i])

i=i+1

fin_mientras
Vectores dinámicos y estáticos
Lo habitual es que un vector tenga una cantidad fija de memoria asignada, aunque
dependiendo del tipo de vector y del lenguaje de programación un vector podría tener una
cantidad variable de datos. En este caso, se les denomina vectores dinámicos, en
oposición, a los vectores con una cantidad fija de memoria asignada se los
denomina vectores estáticos.
El uso de vectores dinámicos requiere realizar una apropiada gestión de memoria
dinámica. Un uso incorrecto de los vectores dinámicos, o mejor dicho, una mala gestión de
la memoria dinámica, puede conducir a una fuga de memoria. Al utilizar vectores
dinámicos siempre habrá que liberar la memoria utilizada cuando ésta ya no se vaya a
seguir utilizando.
Lenguajes más modernos y de más alto nivel, cuentan con un mecanismo denominado
recolector de basura (como es el caso de Java) que permiten que el programa decida si
debe liberar el espacio basándose en si se va a utilizar en el futuro o no un determinado
objeto.
Algoritmo de ordenamiento

Algoritmo de ordenamiento
En computación y matemáticas un algoritmo de ordenamiento es un algoritmo que pone
elementos de una lista o un vector en una secuencia dada por una relación de orden, es
decir, el resultado de salida ha de ser una permutación —o reordenamiento— de la
entrada que satisfaga la relación de orden dada. Las relaciones de orden más usadas son
el orden numérico y el orden lexicográfico. Ordenamientos eficientes son importantes para
optimizar el uso de otros algoritmos (como los de búsqueda y fusión) que requieren listas
ordenadas para una ejecución rápida. También es útil para poner datos en forma canónica
y para generar resultados legibles por humanos.
Desde los comienzos de la computación, el problema del ordenamiento ha atraído gran
cantidad de investigación, tal vez debido a la complejidad de resolverlo eficientemente a
pesar de su planteamiento simple y familiar. Por ejemplo, BubbleSort fue analizado desde
1956. Aunque muchos puedan considerarlo un problema resuelto, nuevos y útiles
algoritmos de ordenamiento se siguen inventado hasta el día de hoy (por ejemplo,
el ordenamiento de biblioteca se publicó por primera vez en el 2004). Los algoritmos de
ordenamiento son comunes en las clases introductorias a la computación, donde la
abundancia de algoritmos para el problema proporciona una gentil introducción a la
variedad de conceptos núcleo de los algoritmos, como notación de O
mayúscula, algoritmos divide y vencerás, estructuras de datos, análisis de los casos peor,
mejor, y promedio, y límites inferiores.
Clasificación
Los algoritmos de ordenamiento se pueden clasificar en las siguientes maneras:

 La más común es clasificar según el lugar donde se realice la ordenación


o Algoritmos de ordenamiento interno: en la memoria del ordenador.
o Algoritmos de ordenamiento externo: en un lugar externo como un disco
duro.

 Por el tiempo que tardan en realizar la ordenación, dadas entradas ya ordenadas


o inversamente ordenadas:
o Algoritmos de ordenación natural: Tarda lo mínimo posible cuando la
entrada está ordenada.
o Algoritmos de ordenación no natural: Tarda lo mínimo posible cuando la
entrada está inversamente ordenada.

 Por estabilidad: un ordenamiento estable mantiene el orden relativo que tenían


originalmente los elementos con claves iguales. Por ejemplo, si una lista ordenada por
fecha se reordena en orden alfabético con un algoritmo estable, todos los elementos
cuya clave alfabética sea la misma quedarán en orden de fecha. Otro caso sería
cuando no interesan las mayúsculas y minúsculas, pero se quiere que si una clave
aBC estaba antes que AbC, en el resultado ambas claves aparezcan juntas y en el
orden original: aBC, AbC. Cuando los elementos son indistinguibles (porque cada
elemento se ordena por la clave completa) la estabilidad no interesa. Los algoritmos de
ordenamiento que no son estables se pueden implementar para que sí lo sean. Una
manera de hacer esto es modificar artificialmente la clave de ordenamiento de modo
que la posición original en la lista participe del ordenamiento en caso de coincidencia.

Los algoritmos se distinguen por las siguientes características:

 Complejidad computacional (peor caso, caso promedio y mejor caso) en términos


de n, el tamaño de la lista o arreglo. Para esto se usa el concepto de orden de una
función y se usa la notación O(n). El mejor comportamiento para ordenar (si no se
aprovecha la estructura de las claves) es O(n log n). Los algoritmos más simples son
cuadráticos, es decir O(n²). Los algoritmos que aprovechan la estructura de las claves
de ordenamiento (p. ej. bucket sort) pueden ordenar en O(kn) donde k es el tamaño
del espacio de claves. Como dicho tamaño es conocido a priori, se puede decir que
estos algoritmos tienen un desempeño lineal, es decir O(n).
 Uso de memoria y otros recursos computacionales. También se usa la notación
O(n).

Estabilidad
Los algoritmos de ordenamiento estable mantienen un relativo preorden total. Esto
significa que un algoritmo es estable solo cuando hay dos registros R y S con la misma
clave y con R apareciendo antes que S en la lista original.
Cuando elementos iguales (indistinguibles entre sí), como números enteros, o más
generalmente, cualquier tipo de dato en donde el elemento entero es la clave, la
estabilidad no es un problema. De todas formas, se asume que los siguientes pares de
números están por ser ordenados por su primer componente:

(4, 1) (3, 7) (3, 1) (5, 6)


En este caso, dos resultados diferentes son posibles, uno de los cuales mantiene un orden
relativo de registros con claves iguales, y una en la que no:

(3, 7) (3, 1) (4, 1) (5, 6) (orden mantenido)

(3, 1) (3, 7) (4, 1) (5, 6) (orden cambiado)


Los algoritmos de ordenamiento inestable pueden cambiar el orden relativo de registros
con claves iguales, pero los algoritmos estables nunca lo hacen. Los algoritmos inestables
pueden ser implementados especialmente para ser estables. Una forma de hacerlo es
extender artificialmente el cotejamiento de claves, para que las comparaciones entre dos
objetos con claves iguales sean decididas usando el orden de las entradas
original. Recordar este orden entre dos objetos con claves iguales es una solución poco
práctica, ya que generalmente acarrea tener almacenamiento adicional.
Ordenar según una clave primaria, secundaria, terciara, etc., puede ser realizado utilizando
cualquier método de ordenamiento, tomando todas las claves en consideración (en otras
palabras, usando una sola clave compuesta). Si un método de ordenamiento es estable,
es posible ordenar múltiples ítems, cada vez con una clave distinta. En este caso, las
claves necesitan estar aplicadas en orden de aumentar la prioridad.
Ejemplo: ordenar pares de números, usando ambos valores

(4, 1) (3, 7) (3, 1) (4, 6) (original)

(4, 1) (3, 1) (4, 6) (3, 7) (después de ser ordenado por el segundo valor)

(3, 1) (3, 7) (4, 1) (4, 6) (después de ser ordenado por el primer valor)
Por otro lado:

(3, 7) (3, 1) (4, 1) (4, 6) (después de ser ordenado por el primer valor)

(3, 1) (4, 1) (4, 6) (3, 7) (después de ser ordenando por el segundo


valor,

el orden por el primer valor es perturbado)


Lista de algoritmos de ordenamiento
Algunos algoritmos de ordenamiento agrupados según estabilidad tomando en cuenta
la complejidad computacional.

Estables

Nombre Memori
Nombre traducido Complejidad Método
original a

Ordenamiento de burbuja Bubblesort O(n²) O(1) Intercambio

Ordenamiento de burbuja
Cocktail sort O(n²) O(1) Intercambio
bidireccional

Ordenamiento por
Selection Sort O(n²) O(1) Intercambio
selección

Ordenamiento por
Insertion sort O(n²) O(1) Inserción
inserción

Ordenamiento por No
Bucket sort O(n) O(n)
casilleros comparativo

No
Ordenamiento por cuentas Counting sort O(n+k) O(n+k)
comparativo

Ordenamiento por mezcla Merge sort O(n log n) O(n) Mezcla

Ordenamiento con árbol


Binary tree sort O(n log n) O(n) Inserción
binario

Pigeonhole sort O(n+k) O(k)

No
Ordenamiento Radix Radix sort O(nk) O(n)
comparativo

Distribution
O(n³) versión recursiva O(n²)
sort

Gnome sort O(n²) O(1)

Inestables

Nombre Memori
Nombre traducido Complejidad Método
original a
Ordenamiento Shell Shell sort O(n )
1.25
O(1) Inserción

Comb sort O(n log n) O(1) Intercambio

Ordenamiento por
Selection sort O(n²) O(1) Selección
selección

Ordenamiento por
Heapsort O(n log n) O(1) Selección
montículos

Smoothsort O(n log n) O(1) Selección

Promedio: O(n log n),
Ordenamiento rápido Quicksort O(log n) Partición
peor caso: O(n²)

Promedio: O(n u),
Several Unique peor caso: O(n²);
Sort u=n; u = número único
de registros

Cuestionables, imprácticos

Nombre Memori
Nombre traducido Complejidad Método
original a

O(n × n!), peor: no
Bogosort
termina

O(n), excepto en
Pancake sorting máquinas de Von
Neumann

Promedio: O(n!) Peor:


Ordenamiento Aleatorio Randomsort
No termina

Ordenamiento por inserción


El ordenamiento por inserción (insertion sort en inglés) es una manera muy natural de
ordenar para un ser humano, y puede usarse fácilmente para ordenar un mazo de cartas
numeradas en forma arbitraria. Requiere O(n²) operaciones para ordenar una lista
de n elementos.
Inicialmente se tiene un solo elemento, que obviamente es un conjunto ordenado.
Después, cuando hay k elementos ordenados de menor a mayor, se toma el
elemento k+1 y se compara con todos los elementos ya ordenados, deteniéndose cuando
se encuentra un elemento menor (todos los elementos mayores han sido desplazados una
posición a la derecha) o cuando ya no se encuentran elementos (todos los elementos
fueron desplazados y este es el más pequeño). En este punto se inserta el
elemento k+1 debiendo desplazarse los demás elementos.
Ejemplo de ordenamiento por inserción ordenando una lista de números aleatorios.
Ordenamiento por intercambio
El ordenamiento por intercambio (Interchange Sort en inglés) es un algoritmo de
ordenamiento que requiere O  operaciones para ordenar una lista de n elementos.

Descripción del algoritmo


Su funcionamiento es el siguiente:

 Se en el primer elemento y compara con la siguiente posicion el elemento


 Si es Menor se Intercambiara con el primero
 Se sigue comparando con el siguiente elemento
 Si es menor Intercambiarlo con el segundo, caso contrario no hace nada ... y asi
sucesivamente.

De esta manera se puede escribir el siguiente pseudocódigo para ordenar una lista
de n elementos indexados desde el 1:

para p=1 hasta n-1


d = p;
para p=p+1 hasta n
si elem[p] < elem[d] entonces
aux = elem[d]
elem[d]=elem[p]
elem[p]=aux
fin si
fin para
fin para

Ordenamiento por selección


El ordenamiento por selección (Selection Sort en inglés) es un algoritmo de
ordenamiento que requiere O  operaciones para ordenar una lista de n elementos.

Descripción del algoritmo


Su funcionamiento es el siguiente:

 Buscar el mínimo elemento de la lista


 Intercambiarlo con el primero
 Buscar el siguiente mínimo en el resto de la lista
 Intercambiarlo con el segundo

Y en general:

 Buscar el mínimo elemento entre una posición i y el final de la lista


 Intercambiar el mínimo con el elemento de la posición i

De esta manera se puede escribir el siguiente pseudocódigo para ordenar una lista
de n elementos indexados desde el 1:

para i=1 hasta n-1


mínimo = i;
para j=i+1 hasta n
si lista[j] < lista[mínimo] entonces
mínimo = j /* (!) */
fin si
fin para
intercambiar(lista[i], lista[mínimo])
fin para
Este algoritmo mejora ligeramente el algoritmo de la burbuja. En el caso de tener que
ordenar un vector de enteros, esta mejora no es muy sustancial, pero cuando hay que
ordenar un vector de estructuras más complejas, la operación intercambiar() sería más
costosa en este caso. Este algoritmo realiza muchas menos
operaciones intercambiar() que el de la burbuja, por lo que lo mejora en algo. Si la línea
comentada con (!) se sustituyera por intercambiar(lista[i], lista[j]) tendríamos una versión
del algoritmo de la burbuja (naturalmente eliminando el orden intercambiar del final).
Otra desventaja de este algoritmo respecto a otros como el de burbuja o de inserción
directa es que no mejora su rendimiento cuando los datos ya están ordenados o
parcialmente ordenados. Así como, por ejemplo, en el caso de la ordenación de burbuja se
requeriría una única pasada para detectar que el vector ya está ordenado y finalizar, en la
ordenación por selección se realizarían el mismo número de pasadas independientemente
de si los datos están ordenados o no.
Ordenamiento de burbuja
La Ordenación de burbuja (Bubble Sort en inglés) es un sencillo algoritmo de
ordenamiento. Funciona revisando cada elemento de la lista que va a ser ordenada con el
siguiente, intercambiándolos de posición si están en el orden equivocado. Es necesario
revisar varias veces toda la lista hasta que no se necesiten más intercambios, lo cual
significa que la lista está ordenada. Este algoritmo obtiene su nombre de la forma con la
que suben por la lista los elementos durante los intercambios, como si fueran pequeñas
"burbujas". Dado que solo usa comparaciones para operar elementos, se lo considera un
algoritmo de comparación, siendo el más sencillo de implementar.
Este algoritmo es esencialmente un algoritmo de fuerza bruta lógica.
Descripción
Una manera simple de expresar el ordenamiento de burbuja en pseudocódigo es la
siguiente:

Este algoritmo realiza el ordenamiento o reordenamiento de una lista a de n valores, en


este caso de n términos numerados del 0 al n-1; consta de dos bucles anidados, uno con
el índice i, que da un tamaño menor al recorrido de la burbuja en sentido inverso de 2 a n,
y un segundo bucle con el índice j, con un recorrido desde 0 hasta n-i, para cada iteración
del primer bucle, que indica el lugar de la burbuja.
La burbuja son dos términos de la lista seguidos, j y j+1, que se comparan: si el primero es
mayor que el segundo sus valores se intercambian.
Esta comparación se repite en el centro de los dos bucles, dando lugar a la postre a una
lista ordenada. Puede verse que el número de repeticiones solo depende de n y no del
orden de los términos, esto es, si pasamos al algoritmo una lista ya ordenada, realizará
todas las comparaciones exactamente igual que para una lista no ordenada. Esta es una
característica de este algoritmo. Luego veremos una variante que evita este inconveniente.
Para comprender el funcionamiento, veamos un ejemplo sencillo:
Tenemos una lista de números que hay que ordenar:

Podemos ver que la lista que tiene cinco términos, luego:

El índice i hará un recorrido de 2 hasta n:

que en este caso será de 2 a 5. Para cada uno de los valores de i, j tomará
sucesivamente los valores de 0 hasta n-i:

Para cada valor de j,


obtenido en ese
orden, se compara el
valor del índice j con
el siguiente:

Si el término j es mayor que el término j+1, los valores se permutan, en caso


contrario se continúa con la iteración.
Para el caso del ejemplo, tenemos que:

Para la primera iteración del primer bucle:

y j tomará los valores de 0 hasta 3:

Cuando j vale 0, se comparan  , el 55 y el 86, dado que 55 < 86, no se


permuta el orden.
Ahora j vale 1 y se comparan   el 86 y el 48. Como 86 > 48, se
permutan, dando lugar a una nueva lista.
Se repite el proceso
hasta que j valga 3,
dando lugar a una lista
parcialmente ordenada.
Podemos ver que el
término de mayor valor
está en el lugar más
alto.
Ahora i vale 3, y j hará un recorrido
de 0 a 2.
Primero j vale 0, se comparan  ,
el 55 y el 48. Como 55 > 48 se permutan dando lugar a la nueva lista.
Para j = 1 se compara el 55 con el 16 y se cambian de orden.
Para j = 2 se compara el 55 y el 82 y se dejan como están, finalizando el
bucle con una lista mejor ordenada. Puede verse que los dos valores más
altos ya ocupan su lugar. No se ha realizado ninguna comparación con el
término cuarto, dado que ya se sabe que después del primer ciclo es el mayor
de la lista.
El algoritmo consiste en comparaciones sucesivas de dos términos
consecutivos ascendiendo de
abajo a arriba en cada iteración,
como la ascensión de las burbujas
de aire en el agua, de ahí el
nombre del procedimiento. En la
primera iteración el recorrido ha
sido completo, en el segundo se
ha dejado él último término, al
tener ya el mayor de los valores,
en los sucesivos sé ira dejando de
realizar las últimas
comparaciones, como se puede
ver.
Ahora ya i vale 4 y j recorrerá los valores de 0
a 1.
Cuando j vale 0, se comparan  , esto es, el 48 y el 16. Dado que 48 es mayor que 16
se permutan los valores, dando lugar a una lista algo más ordenada que la anterior. Desde
esta nueva ordenación, j pasa a valer 1, con lo que se comparan los términos   el 48
y el 55 que quedan en el mismo orden.
En este caso la burbuja ha ascendido menos que en los casos anteriores, y la
lista está ya ordenada, pero el algoritmo tendrá que completarse, realizando
una última iteración.
Hay que tener en cuenta que el bucle realiza un número fijo de repeticiones y
para finalizar tendrán que completarse, aun en el caso extremo, de que la lista
estuviera previamente ordenada.
Por último i vale 5 y j solo puede vale 0, con lo que sólo se realizará una
comparación de   el 16 y el 48, que ya están ordenados y se dejan igual.
Los bucles finalizan y también el procedimiento, dejando
la lista ordenada.
Una variante que finaliza en caso de que la lista esté
ordenada, puede ser la siguiente: como en el ejemplo
anterior, empleando un centinela ordenado, que detecta
que no se ha modificado la lista en un recorrido de la
burbuja, y que por tanto la lista ya está ordenada,
finalizando inmediatamente.

Ordenamiento Shell
El ordenamiento Shell (Shell sort en inglés) es un algoritmo de
ordenamiento. El método se denomina Shell en honor de su inventor Donald
Shell. Su implementación original, requiere O(n2) comparaciones e
intercambios en el peor caso. Un cambio menor presentado en el libro de V.
Pratt produce una implementación con un rendimiento de O(n log2 n) en el
peor caso. Esto es mejor que las O(n2) comparaciones requeridas por
algoritmos simples pero peor que el óptimo O(n log n). Aunque es fácil
desarrollar un sentido intuitivo de cómo funciona este algoritmo, es muy difícil
analizar su tiempo de ejecución.
El Shell sort es una generalización del ordenamiento por inserción, teniendo
en cuenta dos observaciones:

1. El ordenamiento por inserción es eficiente si la entrada está "casi


ordenada".
2. El ordenamiento por inserción es ineficiente, en general, porque mueve
los valores sólo una posición cada vez.

El algoritmo Shell sort mejora el ordenamiento por inserción comparando


elementos separados por un espacio de varias posiciones. Esto permite que
un elemento haga "pasos más grandes" hacia su posición esperada. Los
pasos múltiples sobre los datos se hacen con tamaños de espacio cada vez
más pequeños. El último paso del Shell sort es un simple ordenamiento por
inserción, pero para entonces, ya está garantizado que los datos del vector
están casi ordenados.
Quicksort
El ordenamiento rápido (quicksort en inglés) es un algoritmo creado por el
científico británico en computación C. A. R. Hoare, basado en la técnica
de divide y vencerás, que permite, en promedio, ordenar n elementos en un
tiempo proporcional a n log n.
Descripción del algoritmo
El algoritmo trabaja de la siguiente forma:

 Elegir un elemento de la lista de elementos a ordenar, al que


llamaremos pivote.
 Resituar los demás elementos de la lista a cada lado del pivote, de
manera que a un lado queden todos los menores que él, y al otro los
mayores. Los elementos iguales al pivote pueden ser colocados tanto a
su derecha como a su izquierda, dependiendo de la implementación
deseada. En este momento, el pivote ocupa exactamente el lugar que le
corresponderá en la lista ordenada.
 La lista queda separada en dos sublistas, una formada por los elementos
a la izquierda del pivote, y otra por los elementos a su derecha.
 Repetir este proceso de forma recursiva para cada sublista mientras éstas
contengan más de un elemento. Una vez terminado este proceso todos
los elementos estarán ordenados.

Como se puede suponer, la eficiencia del algoritmo depende de la posición en


la que termine el pivote elegido.

 En el mejor caso, el pivote termina en el centro de la lista, dividiéndola en


dos sublistas de igual tamaño. En este caso, el orden de complejidad del
algoritmo es O(n·log n).
 En el peor caso, el pivote termina en un extremo de la lista. El orden de
complejidad del algoritmo es entonces de O(n²). El peor caso dependerá
de la implementación del algoritmo, aunque habitualmente ocurre en listas
que se encuentran ordenadas, o casi ordenadas. Pero principalmente
depende del pivote, si por ejemplo el algoritmo implementado toma como
pivote siempre el primer elemento del array, y el array que le pasamos
está ordenado, siempre va a generar a su izquierda un array vacío, lo que
es ineficiente.

 En el caso promedio, el orden es O(n·log n).

No es extraño, pues, que la mayoría de optimizaciones que se aplican al


algoritmo se centren en la elección del pivote.
Ordenación por conteo
La ordenación de conteo es un algoritmo de clasificación de enteros para una
colección de objetos que se ordenan según las claves de los objetos.
Pasos

1. Construya una matriz de trabajo C que tenga un tamaño igual al rango


de la matriz de entrada A.
2. Iterar a través de A , asignando C [x] en función del número de veces
que x apareció en A.
3. Transforme C en una matriz donde C [x] se refiere al número de valores
≤ x mediante la iteración a través de la matriz, asignando a cada C [x]
la suma de su valor anterior y todos los valores en C que le preceden.
4. Iterar hacia atrás a través de A , colocando cada valor en una nueva
matriz ordenada B en el índice registrado en C. Esto se hace para
un A [x] dado asignando B [ C [ A [x]]] a A [x], y disminuyendo C [ A [x]]
en caso de que hubiera valores duplicados en la matriz original sin
clasificar.
Ejemplo de clasificación de conteo
Algoritmo de búsqueda

Algoritmo de búsqueda
Un algoritmo de búsqueda es aquel que está diseñado para localizar un elemento con
ciertas propiedades dentro de una estructura de datos; por ejemplo, ubicar el registro
correspondiente a cierta persona en una base de datos, o el mejor movimiento en una
partida de ajedrez.
La variante más simple del problema es la búsqueda de un número en un vector.
Búsqueda dicotómica
Elementos necesarios en una búsqueda :
log2(n) donde n = elementos de la búsqueda
Ejemplo: log2(1.000.000) ≈ 20
Búsqueda secuencial
Se utiliza cuando el vector no está ordenado o no puede ser ordenado previamente.
Consiste en buscar el elemento comparándolo secuencialmente (de ahí su nombre) con
cada elemento del vector hasta encontrarlo, o hasta que se llegue al final. La existencia se
puede asegurar cuando el elemento es localizado, pero no podemos asegurar la no
existencia hasta no haber analizado todos los elementos del vector. A continuación se
muestra el pseudocódigo del algoritmo:

Datos de entrada:

vec: vector en el que se desea buscar el dato

tam: tamaño del vector. Los subíndices válidos van desde 0 hasta tam-1
inclusive. Puede representarse así: vec[0...tam) o vec[0...tam-1].

dato: elemento que se quiere buscar.

Variables

pos: posición actual en el vector

pos = 0

while pos < tam:

if vec[pos] == dato:

Retorne verdadero y/o pos,

else:

pos = pos + 1

Fin (while)
Retorne falso,
Búsqueda dicotómica (binaria)
Se utiliza cuando el vector en el que queremos determinar la existencia de un elemento
está previamente ordenado. Este algoritmo reduce el tiempo de búsqueda
considerablemente, ya que disminuye exponencialmente el número de iteraciones
necesarias.
Está altamente recomendado para buscar en arrays de gran tamaño. Por ejemplo, en uno
conteniendo 50.000.000 elementos, realiza como máximo 26 comparaciones (en el peor
de los casos).
Para implementar este algoritmo se compara el elemento a buscar con un elemento
cualquiera del array (normalmente el elemento central): si el valor de éste es mayor que el
del elemento buscado se repite el procedimiento en la parte del array que va desde el
inicio de éste hasta el elemento tomado, en caso contrario se toma la parte del array que
va desde el elemento tomado hasta el final. De esta manera obtenemos intervalos cada
vez más pequeños, hasta que se obtenga un intervalo indivisible. Si el elemento no se
encuentra dentro de este último entonces se deduce que el elemento buscado no se
encuentra en todo el array.
A continuación se presenta el pseudocódigo del algoritmo, tomando como elemento inicial
el elemento central del array.

Datos de entrada:

vec: vector en el que se desea buscar el dato

tam: tamaño del vector. Los subíndices válidos van desde 0 hasta tam-1
inclusive.

dato: elemento que se quiere buscar.

Variables

centro: subíndice central del intervalo

inf: límite inferior del intervalo

sup: límite superior del intervalo

inf = 0

sup = tam-1

Mientras inf <= sup:

centro = ((sup - inf) / 2) + inf // División entera: se trunca la fracción

Si vec[centro] == dato devolver verdadero y/o pos, de lo contrario:

Si dato < vec[centro] entonces:

sup = centro - 1
En caso contrario:

inf = centro + 1

Fin (Mientras)

Devolver Falso

También podría gustarte