Estructuras de datos: introducción a la ordenación rápida

ordenación rápida

Quick sort es un método de clasificación de intercambio de estructura de árbol binario propuesto por Hoare en 1962. La clasificación rápida es un algoritmo de clasificación de uso común. Su idea básica es dividir la secuencia que se va a clasificar en dos subsecuencias seleccionando un elemento como "valor base". Todos los elementos de son mayores que el valor base. Luego ordene recursivamente las dos subsecuencias por separado y finalmente combine las subsecuencias ordenadas para obtener una secuencia ordenada completa.

Pensamiento

Cualquier elemento en la secuencia de elementos a ordenar se toma como valor de referencia, y el conjunto a ordenar se divide en dos subsecuencias de acuerdo con el código de clasificación. Todos los elementos en la subsecuencia izquierda son más pequeños que el valor de referencia, y todos los elementos en la subsecuencia derecha son mayores que el valor de referencia.Luego, la subsecuencia más a la izquierda repite el proceso hasta que todos los elementos estén dispuestos en las posiciones correspondientes.inserte la descripción de la imagen aquí

versión de salón

Implementación del código de clasificación de un solo paso de la versión Hall

Defina dos subíndices izquierdo y derecho. Suponiendo que keyi está en el extremo izquierdo, entonces la derecha va primero. Deténgase cuando encuentre algo más pequeño que keyi a la derecha. Luego comienza a caminar por la izquierda y detente cuando encuentres algo más grande que keyi a la izquierda. Intercambie los valores izquierdo y derecho. Luego, hasta que los punteros izquierdo y derecho se encuentren, intercambie los valores de left y keyi.
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

void QuickSort(int* arr,int begin, int end)
{
    
    
   	int left = begin;
    int right = end;
    int keyi = left;
    while (left < right)
    {
    
    
        //右边先走
        while (left < right && arr[right] >= arr[keyi])
        {
    
    
            right--;
        }
        while (left < right && arr[left] <= arr[keyi])
        {
    
    
           left++;
        }
        Swap(&arr[left], &arr[right]);
    }
    Swap(&arr[keyi], &arr[left]);
}

Implementación del código de clasificación de la versión Hall

Cuando la ordenación de un solo paso coloca los datos de la primera clavei donde debería estar en cuclillas, la matriz se puede dividir en tres partes, a saber, [begin, keyi-1], keyi, [keyi+1, end]. En este momento, de acuerdo con la idea recursiva de divide y vencerás del árbol binario, los datos del subintervalo se seleccionan nuevamente como keyi en el lado izquierdo para realizar una ordenación de un solo paso hasta que el subintervalo lo haga. no existe. Finalmente, los datos pueden ser ordenados.
inserte la descripción de la imagen aquí

Optimización para arreglos ordenados

Para la clasificación de matrices ya ordenadas, el código anterior tiene un problema fatal. Es decir, la complejidad temporal de la ordenación rápida no optimizada es O(N^2) en el peor de los casos. Cuando la cantidad de datos es grande, habrá un problema de desbordamiento de pila debido a la recursividad. El primer método de optimización se presenta a continuación, es decir, la selección de números aleatorios de keyi.

Tecla de selección de números aleatorios

Al tomar el número aleatorio de la longitud de la matriz como keyi, el problema de la complejidad temporal de la clasificación rápida en el caso de un orden completo o cercano al orden puede evitarse hasta cierto punto. He aquí un vistazo rápido a cómo se maneja
inserte la descripción de la imagen aquí

Toma el medio de tres

Al comparar los valores en las posiciones más a la izquierda, más a la derecha y en el medio, tome el valor medio como el subíndice keyi. De esta manera, el problema de la disminución de la eficiencia de la clasificación rápida puede optimizarse en cierta medida en el caso de pedidos o pedidos parciales.

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

excavación de pozos

Implementación de código de clasificación de un solo paso del método de excavación

Dado que el método del Sr. Hall es relativamente oscuro y difícil de entender, existe el método de excavación que vamos a presentar ahora. Como sugiere el nombre, el método de excavación es una versión optimizada basada en la idea de clasificación rápida. Primero suponga que la clave es el número más a la izquierda, por lo que el hoyo está a la izquierda. Luego comience a caminar desde la derecha, colóquelo en el hoyo cuando encuentre uno más pequeño que la llave y cambie la posición del hoyo a la posición de parada. Lo mismo ocurre con la izquierda.
inserte la descripción de la imagen aquí

void QuickSort1(int* arr, int begin, int end)
{
    
    
    int left = begin;
    int right = end;
    随机选key
    通过随机数来选择key,优化了顺序和逆序的情况下的时间复杂度
    //int randi = left + (rand() % (right - left));
    //
    //Swap(&arr[randi], &arr[left]);

    //三数取中优化
    int ret = GetMidNum(arr, left, right);
    if (left != ret)
        Swap(&arr[ret], &arr[left]);

    //左边做key
    int key = arr[left];
    int hole = left;
    while (left < right)
    {
    
    
        //右边先走
        while (left < right && arr[right] >= key)
        {
    
    
            right--;
        }
        //右边比key小就把它填到坑里,并改变坑位
        arr[hole] = arr[right];
        hole = right;
        while (left < right && arr[left] <= key)
        {
    
    
            left++;
        }
        //左边比key大就把它填到坑里,并改变坑位
        arr[hole] = arr[left];
        hole = left;
    }
    //最后坑位就是key应该蹲的位置
    arr[hole] = key;
}

Implementación del código del método de excavación

inserte la descripción de la imagen aquí

método de puntero hacia adelante y hacia atrás

Implementación de código de clasificación de un solo paso del método de puntero hacia adelante y hacia atrás

La idea de implementación es la siguiente: primero defina dos punteros prev y cur. Utilice la más a la izquierda como clave, y cur encuentra el valor más pequeño. Cuando el contenido señalado por cur es más pequeño que la clave, ++prev, el valor de prev y el valor de cur se intercambian (la posición coincide y no puede ser intercambiado). Cuando el valor encontrado por cur es mayor que key, ++cur. La situación general de este método se divide en dos tipos, uno, prev seguido de cur. 2. Prev y cur están directamente separados por el valor de la clave. Luego, en este momento, el valor más grande que la tecla se girará gradualmente hacia atrás, y el valor más pequeño que la tecla se lanzará gradualmente hacia adelante.
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Implementación de código de método de puntero delantero y trasero

inserte la descripción de la imagen aquí

Optimización entre celdas

Sabemos que la ordenación rápida en realidad usa la idea de dividir y conquistar la estructura de árbol binario para ordenar. En la estructura de árbol binario, cuando la altura es h, consideramos el caso óptimo (árbol binario completo), y los nodos en la última capa son 2^(h-1). Ocupa la mitad de todo el árbol. El número de nodos en la penúltima capa es 2 ^ (h-2), lo que representa el 25% de los nodos en todo el árbol. De hecho, siempre que se elimine la recurrencia de las últimas tres capas, la pérdida causada por la recurrencia generalmente puede reducirse, la eficiencia puede mejorarse y la pérdida de espacio de pila puede reducirse. Para optimizar dentro de un pequeño intervalo ordenado localmente, hay muchos tipos que podemos elegir. Sin embargo, en base a lo que hemos aprendido antes, podemos encontrar que las ventajas de usar la clasificación en montón y la clasificación en colina sobre la optimización entre celdas no son obvias. El primero necesita construir un montón, y el segundo debe clasificarse previamente de acuerdo con la brecha. En realidad, es el más adecuado para elegir la ordenación por inserción para la optimización entre celdas. Porque en el intervalo ordenado localmente, la eficiencia del ordenamiento por inserción es la más alta.
inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

Implementación no recursiva de clasificación rápida

Utilice la pila para almacenar el intervalo que necesita la recursividad y luego simule el orden de la recursividad de acuerdo con la naturaleza de la pila de último en entrar, primero en salir. Convierta el intervalo en subintervalos para continuar con la clasificación. Deje de agregar el subíndice del intervalo a la pila hasta que el intervalo no exista o solo haya un número en el intervalo.
inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

Resumen de las funciones de clasificación rápida

1. La clasificación rápida es una clasificación con un buen rendimiento integral y escenarios de uso amplio.
En segundo lugar, la complejidad temporal de la clasificación rápida es O(N LogN).
inserte la descripción de la imagen aquí
En el mejor de los casos, la complejidad temporal de ordenación rápida es O(nlogn). Cuando la secuencia que se va a ordenar se puede dividir uniformemente en dos subsecuencias, cada operación de división puede dividir aproximadamente la secuencia por igual. En este momento, se registra el número de capas de llamadas recursivas y el número de comparaciones requeridas para cada capa es n, por lo que el número total de comparaciones es O( N
LogN). En el peor de los casos, la complejidad temporal de ordenación rápida es O(n^2). Cuando la secuencia a ordenar ya está ordenada o básicamente ordenada (por ejemplo, la secuencia ya está en orden ascendente, pero el valor de referencia seleccionado es siempre el elemento más pequeño o más grande), cada operación de división solo puede cortar una subsecuencia, y el capa llamada de forma recursiva El número es n, y el número de comparaciones necesarias para cada capa es n, por lo que el número total de comparaciones es n^2. Sin embargo, al elegir aleatoriamente el valor base o usar métodos de optimización como la mediana de tres, se puede reducir la posibilidad del peor de los casos, mejorando así el rendimiento promedio de la ordenación rápida. En aplicaciones prácticas, se suele utilizar la ordenación rápida.
3. La ordenación rápida es una ordenación inestable.

adquisición de código

Supongo que te gusta

Origin blog.csdn.net/m0_71927622/article/details/131339569
Recomendado
Clasificación