[Estructura de datos] Cálculo de la complejidad del tiempo y la complejidad del espacio

Tabla de contenido

complejidad algorítmica

concepto complejo de tiempo

Método de cálculo de la complejidad del tiempo

Notación asintótica para O grande

El concepto de complejidad espacial

Métodos computacionales para la complejidad espacial

Aplicaciones de la Complejidad de Tiempo y Espacio

números que desaparecen

matriz de rotación


complejidad algorítmica

Una vez que el algoritmo se escribe en un programa ejecutable, necesita consumir recursos de tiempo y recursos de espacio (memoria) cuando se ejecuta. Por lo tanto, para medir la calidad de un algoritmo, generalmente se mide a partir de dos dimensiones de tiempo y espacio , así como la complejidad del tiempo y la complejidad del espacio.

La complejidad del tiempo mide principalmente la velocidad de ejecución de un algoritmo, mientras que la complejidad del espacio mide principalmente el espacio adicional requerido para que se ejecute un algoritmo. En los primeros días del desarrollo de las computadoras, las computadoras tenían muy poca capacidad de almacenamiento. Así que estoy muy preocupado por la complejidad del espacio. Sin embargo, con el rápido desarrollo de la industria informática, la capacidad de almacenamiento de las computadoras ha alcanzado un nivel muy alto. Así que ya no necesitamos prestar especial atención a la complejidad espacial de un algoritmo.
 

concepto complejo de tiempo

Definición de complejidad temporal: en informática, la complejidad temporal de un algoritmo es una función que describe cuantitativamente el tiempo de ejecución de ese algoritmo. El tiempo que tarda en ejecutarse un algoritmo, teóricamente, no se puede calcular, y solo puede saberlo si coloca su programa en la máquina y lo ejecuta. Pero, ¿necesitamos probar cada algoritmo en la computadora?Es posible probar todos los algoritmos en la computadora, pero es muy problemático, por lo que se utiliza el método de análisis de la complejidad del tiempo. El tiempo empleado por un algoritmo es proporcional al número de ejecuciones de las declaraciones en él, y el número de ejecuciones de las operaciones básicas en el algoritmo es la complejidad del tiempo del algoritmo.


A saber: encontrar la expresión matemática entre una determinada consulta básica y el tamaño del problema N es calcular la complejidad temporal del algoritmo.

Método de cálculo de la complejidad del tiempo

Calcule cuántas veces se ha ejecutado en total la instrucción ++count en Func1.

void Func1(int N)
{
	int count = 0;
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			++count;
		}
	}
	for (int j = 0; j < N; ++j)
	{
		++count;
	}
	int M = 10;
	while (M--)
	{
		++count;
	}
	printf("%d\n", count);
}

 Podemos calcular por separado los tiempos de ejecución de ++count en Func1 Hay tres bucles en Func1, y el primer bucle es un bucle for anidado.

El número de veces que se ejecuta ++count en el primer ciclo es N*N veces

El número de veces que se ejecuta ++count en el segundo ciclo es N veces

El número de ejecuciones de ++count en el tercer ciclo es M  veces, donde M=10

Esto lleva a la fórmula funcional de la complejidad del tiempo: F(N)=N^2+2*N+10

A través de esta fórmula funcional de complejidad temporal, encontramos que cuando N es mayor, el impacto de estos dos últimos elementos en el resultado es menor:

 En la práctica, cuando calculamos la complejidad del tiempo, no necesariamente necesitamos calcular el número exacto de ejecuciones, sino solo el número aproximado de ejecuciones, por lo que aquí usamos la representación asintótica de O grande
 

Notación asintótica para O grande

Notación Big O (notación Big O): es una notación matemática utilizada para describir el comportamiento .
Derivación del método Big O:

1. Reemplace todas las constantes aditivas en tiempo de ejecución con la constante 1 . 2. En la función de tiempos de ejecución modificados, solo se mantiene el término de mayor orden . 3. Si existe el elemento de mayor orden y no es 1, elimine la constante multiplicada por este elemento y el resultado es el orden O grande.

Ejemplo 1: ¿Calcular la complejidad temporal de Func2?

void Func2(int N)
{
	int count = 0;
	for (int k = 0; k <= 2 * N; k++)
	{
		++count;
	}

	int M = 10;

	while (M--)
	{
		++count;
	}

	printf("%d\n", count);
}

 Primero, según el ejemplo anterior, podemos obtener fácilmente la complejidad temporal exacta de Func2: F(N)=2N+10

Luego, de acuerdo con la representación asintótica de O grande , elimine la constante 2 multiplicada por este elemento y la constante de suma 10, y finalmente obtenga su complejidad espacial: O (N)

Ejemplo 2: ¿Calcular la complejidad temporal de Func3?

void func3(int N, int M)
{
	int count = 0;
	for (int k = 0; k < M; ++k)
	{
		++count;
	}
	
	for (int k = 0; k < N; ++k)
	{
		++count;
	}

	printf("%d\n", count);
}

Esta pregunta no explica la relación de tamaño entre M y N, por lo que la complejidad temporal es: O(M+N)

pero:

Si M es mucho mayor que N , podemos pensar que la complejidad temporal es O(M) ;

Si M es mucho mayor que N , podemos pensar que la complejidad temporal es O(N) ;

Si el tamaño de M y N es similar , podemos pensar que la complejidad temporal es O(N) u O(M) ;

Recordatorio especial: en general, N se usa para incógnitas en los cálculos de complejidad de tiempo, pero también puede ser M, K, etc.

Ejemplo 3: ¿Calcular la complejidad temporal de Func2?

void Func4(int N)
{
	int count = 0;
	for (int k = 0; k < 100; ++k)
	{
		++count;
	}

	printf("%d\n", count);
}

 Esta función no nos da una incógnita, sino que directamente nos da una constante de 100. Algunos amigos pueden pensar que su complejidad temporal es O(100), pero no es así. El primer artículo de la notación asintótica de gran O dice: 1. ¡Reemplace todas las constantes de suma en el tiempo de ejecución con la constante 1,   por lo que la complejidad temporal de la función func4 es O(1) !

Al mismo tiempo, también necesitamos saber que la complejidad temporal O(1) no significa que solo se pueda realizar una operación, sino un número constante de operaciones .

Ejemplo 4: ¿Calcular la complejidad temporal de strchr?

const char* strchr(const char* str, int character);

 En primer lugar, debemos entender el contenido general de strchr:

Según el peor resultado de ejecución, la complejidad temporal de strchr es O(N) (N es la longitud de *str)

Ejemplo 5: ¿Calcular la complejidad temporal de Bubblesort?

void Bubb1eSort(int* a,int n)
{
	assert(a);
	for (size_t end = n; end > 0; --end)
	{
		int exchange = 0;
		for (size_t i = 1; i < end; ++i)
		{
			if (a[i - 1] > a[i])  
			{
				Swap(&a[i - 1],& a[i]);
				exchange = 1;
			}
		}
		if (exchange == 0)
			break;
	}
}

 Primero analicemos el proceso de clasificación de burbujas:

Cuando end=n: la segunda capa del ciclo for se ejecuta N-1 veces

Cuando end=n-1: la segunda capa del bucle for se ejecuta N-2 veces

Cuando end=n-2: la segunda capa del ciclo for se ejecuta N-3 veces

Cuando end=n-3: la segunda capa del ciclo for se ejecuta N-4 veces

 ——

Cuando end=2: La segunda capa del bucle for se ejecuta una vez

El número total de ejecuciones es 1+2+3+4+..+N=N*(N-1)/2

 Entonces, la complejidad temporal de Bubblesort es O (N ^ 2)

Este ejemplo muestra indirectamente que el cálculo de la complejidad del tiempo no solo puede observar unas pocas capas de bucles, sino que también depende de su forma de pensar.

Ejemplo 6: ¿Calcular la complejidad temporal de BinarySearch?

int BinarySearch(int* a,int n,int x)
{
	assert(a);
	int begin = 0;
	int end = n;
	while (begin < end)
	{
		int mid = begin + ((end - begin) >> 1);
		if (a[mid] < x)
			begin = mid + 1;
		else if (a[mid] > x)
			end = mid;
		else
			return mid;
	}
	return-1;
}

Calcular la complejidad del tiempo de la búsqueda binaria también está analizando su peor caso:

El rango a buscar al principio es N, y el rango se duplica cada vez que se busca, es decir, N/2/2/2/....(N>=0)

Entonces, la complejidad temporal de la búsqueda binaria es O (log 2 N)

Ejemplo 7: ¿Cuál es la complejidad temporal de calcular Fac recursivo factorial?

long long Fib(size_t N)
{
	if (N < 3)
		return 1;
	return Fib(N - 1) + Fib(N - 2);
}

 Es un poco engorroso de explicar, así que es mejor mirar la imagen directamente:

El concepto de complejidad espacial

La complejidad del espacio también es una expresión de función matemática, que es una medida del espacio de almacenamiento temporal ocupado  por un algoritmo durante la operación .
La complejidad del espacio no es cuántos bytes ocupa el programa, porque esto no es muy significativo, por lo que la complejidad del espacio se calcula por el número de variables.
Las reglas de cálculo de la complejidad del espacio son básicamente similares a la complejidad práctica, y también se utiliza la notación asintótica de la O grande.
Nota: El espacio de pila (parámetros de almacenamiento, variables locales, cierta información de registro, etc.) requerido por la función en tiempo de ejecución se ha determinado durante la compilación, por lo que la complejidad del espacio está determinada principalmente por el espacio extra solicitado explícitamente por la función en tiempo de ejecución .

Métodos computacionales para la complejidad espacial

Ejemplo 1: ¿Calcular la complejidad espacial de Bubblesort?

​void Bubb1eSort(int* a,int n)
{
	assert(a);
	for (size_t end = n; end > 0; --end)
	{
		int exchange = 0;
		for (size_t i = 1; i < end; ++i)
		{
			if (a[i - 1] > a[i])  
			{
				Swap(&a[i - 1],& a[i]);
				exchange = 1;
			}
		}
		if (exchange == 0)
			break;
	}
}

Solo se definen dos variables en la función: end e i

Aunque end e i se destruirán cada vez que pase el ciclo, cuando el ciclo comience nuevamente, defina  end  nuevamente y aún usan el espacio original, por lo que el espacio adicional utilizado por la función es 2 .

Entonces la complejidad espacial de esta pregunta es: O(1)

Ejemplo 2: ¿Calcular la complejidad espacial de Fibonacci?

Devuelve los primeros n términos de la sucesión de Fibonacci

1ong 1ong * Fibonacci(size_t n)
{
	if (n == 0)
		return NULL;

	1ong 1ong * fibArray = (1ong 1ong*)ma11oc((n + 1) * sizeof(1ong 1ong));
	fibArray[0] = 0;
	fibArray[1] = 1;
	for (inti = 2; i <= n; ++i)
	{
		fibArray[i] = fibArray[i - 1] + fibArray[i - 2];
	}

	return fibArray;
}

 Obviamente, la complejidad del espacio es O(N), porque se abre un arreglo de n+1

 Por cierto, calcule su complejidad temporal: solo hay una capa de bucle for en la función O(N) 

Ejemplo 3: Calcular la complejidad espacial del factorial recursivo Fac?

1ong 1ong Fac(size_t N)
{
	if (N == 1)
		return 1;

	return Fac(N - 1) * N;
}

Cada recursión debe crear un marco de pila, y cada marco de pila crea un número constante de variables y recurre n veces para crear n marcos de pila, por lo que la complejidad del espacio es O (N)

La complejidad espacial de las funciones recursivas está relacionada con la profundidad de la recursividad.

Aplicaciones de la Complejidad de Tiempo y Espacio

números que desaparecen

Enlace al título original: Likou

 Ideas:

 Método 1 : primero ordene los elementos en la matriz, necesita usar qsort para ordenar rápidamente, y la complejidad del tiempo es O (n * log2N)

 Método 2 : Primero encuentre la suma de los primeros n elementos y sum=(1+n)*n/2 , y luego el número requerido x=sum-(a[0]+a[1]+a[2]+ ...+ a[n-1]) , la complejidad del tiempo es O(N), y la complejidad del espacio es O(1)

Método 3 : Cree una matriz de longitud n, escriba el valor en la posición donde está el valor mediano de la matriz original y luego recorra la matriz para encontrar el elemento que no tiene valor. La complejidad del tiempo es O (N), y el espacio es complejo el grado es O(N)

Método 4 : proporcione un valor x = 0, permita que x sea XORed con 0 ~ n primero, luego permita que x sea XORed con cada valor en la matriz, y finalmente x es el número requerido, y la complejidad de tiempo es O (N)

Usa el método cuatro para resolver el problema:

int missingNumber(int* nums, int numsSize)
{
    int x=0;
    //跟[0,n]异或
    for(int i=1;i<=numsSize;i++)
    {
        x^=i;
    }
    //在跟数组中值异或
    for(int i=0;i<numsSize;i++)
    {
        x^=nums[i];
    }
    return x;
}


 

matriz de rotación

Enlace al título original: Likou

 Método 1 : solución violenta, rotar k veces la complejidad del tiempo es O(N*K), la complejidad del espacio es O(1)

Método 2 : abrir espacio adicional para intercambiar espacio por tiempo. La complejidad del tiempo es O(N) y la complejidad del espacio es O(N)

 

 Método 3 : primero invierta el primer nk, luego invierta el último k y finalmente invierta el todo, la complejidad del tiempo es O (N) y la complejidad del espacio es O (1)  

Usa el método tres para resolver el problema:

void reverse(int*nums,int left,int right)
{
    while(left<right)
    {
        int tem=nums[left];
        nums[left]=nums[right];
        nums[right]=tem;
        ++left;
        --right;
    }
} 

void rotate(int*nums,int numsSize,int k)
{
    if(k>=numsSize) k%=numsSize;
    //前n-k个数逆置
    reverse(nums,0,numsSize-k-1);
    //后k个逆置
    reverse(nums,numsSize-k,numsSize-1);
    //整体逆置
    reverse(nums,0,numsSize-1);
}

El texto completo ha terminado~~

Supongo que te gusta

Origin blog.csdn.net/m0_73648729/article/details/129462466
Recomendado
Clasificación