Ordenamiento rápido (quicksort)

  • Ciencias de la Computación
  • Ordenamiento Rápido…
  • Quicksort algoritmo
  • partición de Lomuto
  • complejidad de Quicksort
  • elección del pivote…

Podcast

Desentrañando el Algoritmo Quicksort0:00 / 10:22
0:001:00 restante

El Ordenamiento Rápido, conocido comúnmente como Quicksort, es uno de los algoritmos de ordenamiento más eficientes y ampliamente utilizados en la informática. Desarrollado por Tony Hoare en 1959, este método se basa en la poderosa estrategia de "divide y vencerás". Es una herramienta fundamental para entender cómo se organizan grandes volúmenes de datos de manera eficiente. Este artículo te guiará a través de su funcionamiento, ventajas y consideraciones.

El principio de "divide y vencerás" implica descomponer un problema complejo en subproblemas más pequeños y manejables. Una vez resueltos estos subproblemas, sus soluciones se combinan para resolver el problema original. Esta filosofía es clave para la eficiencia de Quicksort.

Quicksort: ¿Qué es y cómo funciona el ordenamiento rápido?

La idea central del Ordenamiento Rápido (Quicksort) es seleccionar un elemento de la lista, llamado pivote, y reorganizar los demás elementos. La reorganización se realiza de tal manera que todos los elementos menores que el pivote queden antes que él, y todos los elementos mayores queden después. Este proceso se denomina partición.

Una vez que el pivote está en su posición final, el algoritmo se aplica recursivamente. Se ordena la sublista de elementos menores y la de elementos mayores hasta que toda la lista esté ordenada. El algoritmo termina cuando una sublista tiene cero o un elemento, ya que estas se consideran ordenadas por definición.

Comprueba lo que ya sabes

Test · 20 preguntas · las 3 primeras gratis

Pregunta 1 de 30%

Elegir el elemento central como pivote en Quicksort elimina completamente la posibilidad de que el pivote sea muy pequeño o muy grande.

Practícalo con tarjetas

40 tarjetas · las 3 primeras gratis, sin cuenta

1 / 40

¿Qué es el Ordenamiento Rápido o Quicksort?

Es uno de los algoritmos de ordenamiento más eficientes y ampliamente utilizados en la informática, basado en el paradigma de "divide y vencerás".

Toca para girar · Desliza para navegar

El Proceso de Partición: Esquema de Lomuto paso a paso

Existen varias formas de implementar la partición en Quicksort, pero una de las más comunes y fáciles de entender es el esquema de partición de Lomuto. Este esquema generalmente selecciona el último elemento de la sublista como pivote. Luego, recorre la sublista para colocar los elementos en su posición correcta.

El proceso de Lomuto mantiene un puntero j que avanza desde el inicio hasta el elemento justo antes del pivote. Otro puntero i rastrea el final de la sección de elementos menores o iguales al pivote. Esta mecánica garantiza que, al final, el pivote se ubique correctamente.

Los pasos del esquema de partición de Lomuto son los siguientes:

  1. Se elige un elemento como pivote. Tradicionalmente, en el esquema de Lomuto, se selecciona el último elemento de la sublista arr[alto].
  2. Se inicializa un índice i (índice del elemento más pequeño) a bajo - 1.
  3. Se recorre la sublista con un índice j desde bajo hasta alto - 1.
  4. Si arr[j] es menor o igual que el pivote: a. Se incrementa i. b. Se intercambian arr[i] y arr[j].
  5. Después de que el bucle termina, se intercambia arr[i + 1] con el pivote (arr[alto]).
  6. La función de partición devuelve el índice i + 1, que es la posición final del pivote.

Ejemplo Detallado de Partición de Lomuto en Quicksort

Consideremos la siguiente lista de números enteros que queremos ordenar: [7, 2, 1, 6, 8, 5, 3, 4]. Vamos a aplicar el esquema de partición de Lomuto a esta lista, con bajo = 0 y alto = 7.

  1. Selección del pivote: El pivote es el último elemento, arr[7] = 4.
  2. Inicialización: i = bajo - 1 = 0 - 1 = -1.
  3. Recorrido con j desde 0 hasta 6 (alto - 1):
  • j = 0: arr[0] = 7. ¿7 ≤ 4? No. La lista permanece [7, 2, 1, 6, 8, 5, 3, 4]. i = -1.
  • j = 1: arr[1] = 2. ¿2 ≤ 4? Sí. Se incrementa i a 0. Se intercambian arr[0] y arr[1]. La lista se convierte en [2, 7, 1, 6, 8, 5, 3, 4]. i = 0.
  • j = 2: arr[2] = 1. ¿1 ≤ 4? Sí. Se incrementa i a 1. Se intercambian arr[1] y arr[2]. La lista se convierte en [2, 1, 7, 6, 8, 5, 3, 4]. i = 1.
  • j = 3: arr[3] = 6. ¿6 ≤ 4? No. La lista permanece [2, 1, 7, 6, 8, 5, 3, 4]. i = 1.
  • j = 4: arr[4] = 8. ¿8 ≤ 4? No. La lista permanece [2, 1, 7, 6, 8, 5, 3, 4]. i = 1.
  • j = 5: arr[5] = 5. ¿5 ≤ 4? No. La lista permanece [2, 1, 7, 6, 8, 5, 3, 4]. i = 1.
  • j = 6: arr[6] = 3. ¿3 ≤ 4? Sí. Se incrementa i a 2. Se intercambian arr[2] y arr[6]. La lista se convierte en [2, 1, 3, 6, 8, 5, 7, 4]. i = 2.
  1. Fin del bucle. Ahora i = 2. Se intercambia arr[i + 1] (que es arr[3] = 6) con el pivote arr[alto] (que es arr[7] = 4). La lista se convierte en [2, 1, 3, 4, 8, 5, 7, 6].

  2. Resultado: El pivote 4 está en su posición final en el índice 3. Los elementos a su izquierda (2, 1, 3) son menores que 4. Los elementos a su derecha (8, 5, 7, 6) son mayores que 4. La función de partición devuelve 3.

Impacto del Pivote en Quicksort: Estrategias clave

La elección del pivote es crucial para el rendimiento de Quicksort. Una mala selección puede llevar al peor caso de complejidad. Analicemos las estrategias comunes:

  • Primer elemento: Simple, pero si la lista ya está ordenada o casi ordenada, conduce al peor caso.
  • Último elemento: Usado en Lomuto, similar al primer elemento en términos de riesgo si la lista está pre-ordenada.
  • Elemento central: Reduce la probabilidad de elegir un pivote muy pequeño o muy grande, pero no la elimina.
  • Mediana de tres: Se eligen tres elementos (por ejemplo, el primero, el central y el último) y se usa la mediana de estos como pivote. Esto es una mejora significativa, ya que es menos probable que el pivote sea un extremo.
  • Pivote aleatorio: Se elige un elemento al azar como pivote. Esto ayuda a evitar el peor caso en promedio, ya que la probabilidad de elegir consistentemente un mal pivote es baja.

Complejidad de Quicksort: Casos promedio, peor y mejor

La eficiencia de Quicksort se mide en términos de su complejidad temporal, que describe cómo el tiempo de ejecución crece con el tamaño de la entrada n.

  • Caso Promedio: La complejidad temporal promedio de Quicksort es O(n log n). Esto ocurre cuando el pivote divide la lista en dos sublistas de tamaños aproximadamente iguales. En cada nivel de recursión, se realiza un trabajo de partición proporcional a n, y hay log n niveles de recursión. Por ejemplo, si n = 1024, log₂ n = 10, lo que significa que el algoritmo realiza aproximadamente 1024 · 10 = 10240 operaciones, un número muy eficiente.

  • Peor Caso: La complejidad temporal en el peor caso es O(n²). Esto sucede cuando el pivote seleccionado es siempre el elemento más pequeño o el más grande de la sublista. En esta situación, una de las sublistas resultantes de la partición estará vacía o tendrá un solo elemento. Esto implica que el algoritmo no divide el problema de manera efectiva, realizando n niveles de recursión. Para n = 1024, n² = 1048576, un número significativamente mayor que en el caso promedio.

  • Mejor Caso: La complejidad temporal en el mejor caso también es O(n log n), y ocurre cuando el pivote siempre divide la lista exactamente por la mitad. Esto maximiza la eficiencia de la estrategia "divide y vencerás".

En cuanto a la complejidad espacial, Quicksort es un algoritmo de ordenamiento in situ (o casi in situ). Esto significa que requiere una cantidad mínima de memoria adicional. Su complejidad espacial es O(log n) en el caso promedio (debido a la pila de llamadas recursivas) y O(n) en el peor caso (cuando la recursión es muy profunda).

Pseudo-código del Algoritmo Quicksort para estudiantes

Comprender el pseudo-código es fundamental para implementar el algoritmo. Aquí te mostramos la estructura básica de Quicksort y la función de partición de Lomuto.

funcion quicksort(arr, bajo, alto):
 si bajo < alto:
 // pi es el índice de partición, arr[pi] está ahora en su lugar correcto
 pi = particion(arr, bajo, alto)

 // Ordena recursivamente los elementos antes y después de la partición
 quicksort(arr, bajo, pi - 1)
 quicksort(arr, pi + 1, alto)

funcion particion(arr, bajo, alto):
 // Elige el pivote (último elemento en este esquema)
 pivote = arr[alto]
 // Índice del elemento más pequeño
 i = (bajo - 1)

 para j desde bajo hasta alto - 1:
 // Si el elemento actual es menor o igual que el pivote
 si arr[j] <= pivote:
 i = i + 1
 // Intercambia arr[i] y arr[j]
 intercambiar(arr, i, j)

 // Intercambia arr[i + 1] y arr[alto] (el pivote)
 intercambiar(arr, i + 1, alto)
 retornar i + 1

funcion intercambiar(arr, idx1, idx2):
 temp = arr[idx1]
 arr[idx1] = arr[idx2]
 arr[idx2] = temp

Quicksort: Evita estos errores comunes al aprender

Al estudiar Quicksort, es fácil caer en ciertos errores o malinterpretaciones. Conocerlos te ayudará a consolidar tu entendimiento:

  1. Confundir el pivote con el elemento central: El pivote no es necesariamente el elemento en el medio de la lista. Es un elemento elegido para la partición, que puede ser el primero, el último, uno aleatorio, o la mediana de tres, entre otros.
  2. Creer que Quicksort siempre es el más rápido: Aunque su caso promedio es excelente, el peor caso de O(n²) puede ser muy lento. Otros algoritmos como Mergesort tienen una complejidad O(n log n) garantizada en todos los casos, aunque con mayor uso de memoria.
  3. Implementación incorrecta de la partición: Un error común es no manejar correctamente los índices o no asegurar que todos los elementos menores queden a un lado y los mayores al otro. Esto puede llevar a bucles infinitos o resultados incorrectos.
  4. Problemas de pila de recursión: En lenguajes con límites de recursión, un Quicksort ingenuo en el peor caso puede agotar la pila de llamadas, especialmente con listas muy grandes. Esto se puede mitigar con optimizaciones como la recursión de cola o el uso de un bucle iterativo para la sublista más grande.

Simulación interactiva de Quicksort: Aprendizaje práctico

Una simulación interactiva del Ordenamiento Rápido es una herramienta invaluable. Permite visualizar el proceso paso a paso, lo cual es fundamental para comprender su funcionamiento. Al interactuar con la simulación, se pueden observar y probar los siguientes aspectos:

  • Ejecución paso a paso de la partición según el pivote: La simulación mostrará cómo se selecciona el pivote y cómo los elementos se reorganizan alrededor de él. Se podrá ver el movimiento de los punteros i y j (si se usa el esquema de Lomuto o Hoare) y los intercambios de elementos. Esto ayuda a internalizar el mecanismo exacto de la partición.
  • Impacto de la elección del pivote: Se puede experimentar con diferentes estrategias de selección de pivote (primer elemento, último, aleatorio, mediana de tres) y observar cómo afecta la eficiencia de la partición y el número de niveles de recursión. Por ejemplo, al elegir el primer elemento como pivote en una lista ya ordenada, se verá cómo el algoritmo realiza muchas particiones desequilibradas, llevando al peor caso.
  • Visualización de la recursión: La simulación puede mostrar cómo la lista se divide en sublistas y cómo el algoritmo se aplica a cada una de ellas de forma recursiva. Esto clarifica el concepto de "divide y vencerás".
  • Comparación de rendimiento: Algunas simulaciones permiten comparar el número de comparaciones y movimientos de elementos para diferentes entradas o elecciones de pivote. Esto ilustra la diferencia entre el caso promedio y el peor caso de O(n log n) y O(n²), respectivamente.

Al manipular la simulación, un estudiante puede obtener una comprensión intuitiva de por qué Quicksort es tan eficiente en la práctica. También puede identificar cuáles son las condiciones que pueden degradar su rendimiento, reforzando los conceptos teóricos con la experiencia práctica.

Preguntas Frecuentes sobre Quicksort

¿Por qué se llama "Ordenamiento Rápido"?

Se le llama "Ordenamiento Rápido" porque, en su caso promedio, es uno de los algoritmos de ordenamiento basados en comparaciones más veloces y eficientes. Su complejidad temporal de O(n log n) lo hace muy competitivo para grandes conjuntos de datos.

¿Quicksort es siempre la mejor opción para ordenar?

No siempre. Aunque su caso promedio es excelente, su peor caso de O(n²) lo hace menos ideal en situaciones donde la estabilidad del rendimiento es crítica. En esos casos, algoritmos como Mergesort, que garantizan O(n log n) en todos los escenarios, podrían ser preferibles, a pesar de su mayor uso de memoria.

¿Qué es el "pivote" en Quicksort y por qué es importante?

El pivote es un elemento seleccionado de la lista que se utiliza para dividirla en dos sublistas: una con elementos menores que el pivote y otra con elementos mayores. Su elección es crucial porque una buena selección (que divide la lista de manera equilibrada) optimiza el rendimiento del algoritmo, mientras que una mala elección puede llevar al peor caso.

Practícalo, no solo lo leas

40 tarjetas, un test con explicaciones y un pódcast sobre este tema. La cuenta es gratis y sigues justo donde lo dejaste.

  • Sin tarjeta de crédito
  • Web y app móvil
  • 330.000+ estudiantes
Empezar gratis

Temas relacionados