"Algoritmos de ordenación" Recursividad y no recursividad de la ordenación rápida

1. El enfoque de este capítulo

  1. pensamiento rápido

  2. Tres formas de lograr una sola fila rápida (hoare, excavación, punteros delanteros y traseros)

  3. Implementación recursiva de quicksort

  4. Cálculo de la complejidad del tiempo del algoritmo recursivo Quicksort

  5. Optimice el ordenamiento rápido (medio de tres números, optimización entre celdas)

  6. Implementación no recursiva de cola rápida (implementación de pila o cola)

2. Cola Rápida

2.1 La idea del remo rápido

Quicksort es esencialmente una ordenación de intercambio. Comencemos con una perspectiva de paso único: la ordenación de paso único de ordenación rápida le permitirá elegir la clave que se colocará en la posición correcta de la matriz. ¿Cuál es la posición correcta? Es decir, después de ordenar en una sola pasada, el número (Clave) ya se ha ordenado y no hay necesidad de cambiarlo más tarde. ¿Cómo asegurarse de que esté en la posición correcta? Siempre que todos los números a su izquierda sean menores o iguales que él, y todos los números a su derecha sean mayores o iguales, entonces está en la posición correcta. (orden ascendente).

Pasos de un solo paso de ordenación rápida: seleccione un número clave de la matriz, generalmente elija el número más a la izquierda o más a la derecha, aquí elijo el número más a la izquierda en la matriz, por ejemplo: 5 3 2 8 6 1 10 9 3 4 7, donde Keyi es 0, a[keyi] es 5

¿Cómo cambio los elementos de la matriz para que el 5 esté en la posición correcta?

2.2 Tres tipos de clasificación de un solo paso

Hay tres algoritmos de clasificación de un solo paso para la clasificación rápida:

El primero: hoare , escrito por el primer inventor del algoritmo quicksort-----Tony Hoare (Tony  Hoare )

Este método es: primero seleccione un Keyi, tome dos variables enteras izquierda y derecha, estas dos variables enteras representan los subíndices de la matriz, e inicialmente apuntan a 0 y n-1 respectivamente. Luego deje que la derecha se mueva primero, busque un número mayor que a[keyi], luego deténgase a la derecha, deje que la izquierda se mueva, busque un número menor que a[keyi], deténgase y luego intercambie a[left] y a[right], si En el camino del movimiento hacia la derecha o hacia la izquierda, derecha==izquierda, es decir, cuando la derecha y la izquierda se encuentran, la derecha y la izquierda deben apuntar a un número menor que a[keyi]. Luego se intercambian a[keyi] y este número más pequeño (puntos de encuentro), y terminan con a[keyi] en la posición correcta, es decir, todos los números a la izquierda son menores o iguales que él, y todos los números a la derecha son mayores o iguales que él.

Icono:

 Cabe señalar que: primero debe dejar ir a la derecha y luego a la izquierda. De lo contrario, el punto donde se encuentran no puede ser un número menor que a[keyi].

Código de referencia:

int Q_PartSort1(int* a, int begin, int end)//hoare
{
	int keyi = begin;
	int left = begin;
	int right = end;
	while (left < right)
	{
		while (left < right && a[right] >= a[keyi])//右边找小于a[keyi]的数
		{
			right--;
		}
		while (left < right && a[left] <= a[keyi])//左边找大于a[keyi]的数
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	//将a[keyi]与相遇点交换(要保证相遇点比a[keyi]小,需要让right先走)
	Swap(&a[keyi], &a[right]);
	return right;
}

El segundo: método de excavación.

Pasos: primero guarde el valor de a[keyi] en int temp, luego haga keyi primero, int left=0, int right=n-1

Deje que la derecha vaya primero, busque un número menor que a[keyi], colóquelo en un[agujero] y actualice hole=right. Luego, la izquierda va de nuevo, encuentra un número mayor que a[keyi], luego lo coloca en un [agujero], actualiza el agujero = izquierda nuevamente, luego se mueve a la derecha y la izquierda se mueve hasta que la izquierda es igual a la derecha. En este punto, el punto de encuentro debe ser un hoyo, y finalmente se coloca la temperatura en un [agujero].

Icono:

 A diferencia de hoare, no es necesario garantizar que el valor del punto de encuentro sea menor que la temperatura.

Código de referencia:

int Q_PartSort2(int* a, int begin, int end)//挖坑法
{
	int key = a[begin];
	int hole = begin;
	int left = begin;
	int right = end;
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			right--;
		}
		a[hole] = a[right];
		hole = right;
		while (left < right && a[left] <= key)
		{
			left++;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	return hole;
}

El tercer tipo: método de puntero delantero y trasero.

Tome la tecla izquierda del subíndice más a la izquierdai, prev=begin, next=begin+1

A continuación, encuentre el número pequeño. Si encuentra un número menor que a[keyi], deje prev++, y luego intercambie a[prev] y a[next].

Fin hasta que el siguiente sea mayor que n.

Finalmente, deje que a[keyi] y a[prev] se intercambien.

Icono:

 Código de referencia:

int Q_PartSort3(int* a, int begin, int end)//前后指针法
{
	int keyi = begin;
	int prev = begin;
	int next = begin + 1;
	while (next <= end)
	{
		if (a[next] < a[keyi] && ++prev != next)
		{
			Swap(&a[prev], &a[next]);
		}
		next++;
	}
	Swap(&a[keyi], &a[prev]);
	return prev;
}

Todos debemos dominar las tres clasificaciones de un solo paso y, a veces, examinaremos las siguientes preguntas

Debe tenerse en cuenta que, aunque los resultados del conjunto de datos anterior son los mismos después de las tres clasificaciones de un solo paso, esto es una coincidencia. Si se agregan más datos, los resultados pueden ser diferentes después de un solo paso.

Supongamos que un conjunto de secuencias de palabras clave de registro inicial son (65, 56, 72, 99, 86, 25, 34, 66), entonces el resultado de una ordenación rápida basada en la primera palabra clave 65 es ()
34, 56, 25, 65, 86, 99, 72, 66
B 25, 34, 56, 65, 99, 86, 72, 66
34, 56, 25, 65, 66, 99, 86, 72
D 34,56,25,65,99,86,72,66
La pregunta no especifica qué clasificación rápida de un solo paso usar. Para tal pregunta, debe probar los tres pasos únicos.

2.3 Implementación recursiva de quicksort

Ponga el código de referencia primero y luego dibujaremos una imagen del proceso recursivo.

 Código de referencia:

void _QuickSort1(int* a,int begin,int end)//递归
{
	if (begin >= end)
	{
		return;
	}
	int keyi = Q_PartSort2(a, begin, end);

	_QuickSort1(a, begin, keyi - 1);
	_QuickSort1(a, keyi + 1, end);
}

 Por razones de espacio, no se dibuja la mitad derecha.

2.4 Cálculo de la complejidad temporal del algoritmo recursivo de clasificación rápida

Peor caso: ordenado

 El número aproximado de ejecuciones es T(N) = N+N-1+N-2+.....+3+2+1

La complejidad del tiempo es O(N*N)

Mejor caso: la clave tomada cada vez es la mediana

Equivalente a un árbol binario completo con una altura de logN

La complejidad del tiempo es N*logN

2.5 Optimizar la ordenación rápida

2.5.1 Optimización 1: Tomar el medio de tres números

Sabemos que las matrices ordenadas no son buenas para una clasificación rápida.Desde esta perspectiva, tenemos el método de optimización de tomar el medio de los tres números:

Es decir, seleccione mid=(left+right)/2

Entre los tres números a[left], a[right] y a[mid], el valor es el número de la mediana, y luego se intercambia con a[keyi].

 Código de referencia:

int GetMidIndex(int* a, int begin, int end)
{
	int mid = begin + ((end - begin)>>1);
	if (
		(a[mid] >= a[begin] && a[mid] <= a[end])
		|| 
		(a[mid]>=a[end] && a[mid] <= a[begin])
		)
	{
		return mid;
	}
	if (
		(a[begin]<=a[mid] && a[begin]>=a[end])
		||
		(a[begin] >= a[mid] && a[begin] <= a[end])
		)
	{
		return begin;
	}
	return end;
}

int Q_PartSort3(int* a, int begin, int end)//前后指针法
{
	//三数取中优化
	int ki = GetMidIndex(a, begin, end);
	Swap(&a[begin], &a[ki]);
	int keyi = begin;
	int prev = begin;
	int next = begin + 1;
	while (next <= end)
	{
		if (a[next] < a[keyi] && ++prev != next)
		{
			Swap(&a[prev], &a[next]);
		}
		next++;
	}
	Swap(&a[keyi], &a[prev]);
	return prev;
}

2.5.2 Optimización entre celdas

Cuando el intervalo es muy pequeño, la ordenación por inserción se usa directamente y no hay necesidad de continuar con la recursividad.

 Código de referencia:

void _QuickSort1(int* a,int begin,int end)//递归
{
	//小区间优化
	if (end - begin + 1 <= 12)
	{
		InsertSort(a, end - begin + 1);
	}
	if (begin >= end)
	{
		return;
	}
	int keyi = Q_PartSort2(a, begin, end);

	_QuickSort1(a, begin, keyi - 1);
	_QuickSort1(a, keyi + 1, end);
}

Finalmente, la clasificación general es pasar A y N. Para no pasar el intervalo, aquí se agrega una capa de encapsulación.

void QuickSort(int* a, int n)
{
	_QuickSort1(a, 0, n - 1);//递归
}

2.6 Clasificación rápida no recursiva

Cuando el número a ordenar es grande, puede causar un desbordamiento de la pila, por lo que se requiere un algoritmo de ordenación rápida no recursivo.

Aquí, stack + loop se usa para simular el proceso de llamada recursivo, y la eficiencia del tiempo no es muy diferente de la recursividad de llamada.

Esencialmente el mismo procedimiento que llamar a la recursividad

 Código de referencia:

void _QuickSort2(int* a, int begin, int end)//非递归
{
	ST st;
	STInit(&st);
    //检查传递的end和begin
	if (end > begin)
	{
		STPush(&st, begin);
		STPush(&st, end);
	}
	while (!STEmpty(&st))
	{
		int right = STRear(&st);
		STPop(&st);
		int left = STRear(&st);
		STPop(&st);
		int mid = Q_PartSort2(a, left, right);
        if(left<mid-1)
        {
            STPush(&st, left);
	    	STPush(&st, mid - 1);
        }    

		if(mid+1<right)
        {
            STPush(&st, mid + 1);
			STPush(&st, right);
        }
	}
	STDestroy(&st);
}

La cola implementa un algoritmo no recursivo de clasificación rápida:

 Código de referencia:

void _QuickSort3(int* a, int begin, int end)//非递归
{
	Queue q;
	QueueInit(&q);
	if (end > begin)
	{
		QueuePush(&q, begin);
		QueuePush(&q, end);
	}
	while (!QueueEmpty(&q))
	{
		int left = QueueFront(&q);
		QueuePop(&q);
		int right = QueueFront(&q);
		QueuePop(&q);
		int keyi = Q_PartSort1(a, left, right);
		if (left < keyi-1)
		{
			QueuePush(&q, left);
			QueuePush(&q, keyi-1);
		}
		if (keyi + 1 < right)
		{
			QueuePush(&q, keyi+1);
			QueuePush(&q, right);
		}
	}
	QueueDestroy(&q);
}

Supongo que te gusta

Origin blog.csdn.net/m0_62171658/article/details/124313424
Recomendado
Clasificación