Complejidad algorítmica: notas introductorias sobre algoritmos y estructuras de datos (2)

Logotipo de CSDNCorreo

Este artículo es la segunda parte de las notas de estudio sobre algoritmos y estructuras de datos, y se seguirá actualizando. Los amigos son bienvenidos a leer y aprender. Si hay algo que no entiendo o está mal, por favor comunicar

¿Qué es la complejidad algorítmica?

La complejidad algorítmica está diseñada para calcular el volumen de datos de entrada NNEn el caso de N , el "uso de tiempo" y el "uso de espacio" del algoritmo; refleja el tiempo y el espacio utilizados por el algoritmo para ejecutarse con el "tamaño de datosNNN " y aumentar la velocidad.

La complejidad del algoritmo se puede evaluar desde dos perspectivas de tiempo y espacio :

  • Tiempo : suponiendo que el tiempo de ejecución de cada operación es una constante fija, el "número de operaciones informáticas" realizadas por el algoritmo se cuenta para representar el tiempo necesario para que se ejecute el algoritmo;
  • Espacio : cuente el "espacio máximo" requerido para que el algoritmo se ejecute en el peor de los casos;

"Tamaño de datos de entrada" NNN se refiere a la cantidad de datos de entrada procesados ​​por el algoritmo; según los diferentes algoritmos, tiene diferentes definiciones, por ejemplo:

  • Algoritmo de clasificación : NNN representa el número de elementos a ordenar;
  • Algoritmo de búsqueda : NNN representa el número total de elementos en el rango de búsqueda, como el tamaño de la matriz, el tamaño de la matriz, el número de nodos del árbol binario, los nodos y los bordes del gráfico, etc.;

A continuación, presentaremos la "complejidad del tiempo" y la "complejidad del espacio" desde la perspectiva de la definición del concepto, la representación simbólica, las reglas de análisis, los tipos comunes, el análisis de ejemplos y las compensaciones espacio-temporales.

complejidad del tiempo

definición del concepto

Por definición, la complejidad del tiempo se refiere al tamaño de los datos de entrada es NNCuando N , el tiempo que tarda en ejecutarse el algoritmo. requiere atención:

  • Lo que se cuenta es el "número de operaciones informáticas" del algoritmo, no el "tiempo de ejecución absoluto". El número de operaciones computacionales y el tiempo absoluto de ejecución están correlacionados positivamente, pero no son iguales. El tiempo de ejecución del algoritmo se ve afectado por varios factores, como "lenguaje de programación, velocidad del procesador de la computadora y entorno operativo". Por ejemplo, el mismo algoritmo tendrá diferentes tiempos de ejecución si se implementa en Python o C++, usando una CPU o GPU, usando un IDE local o una plataforma en línea.
  • Refleja que el número de operaciones de cálculo varía con el tamaño de los datos NN¿Qué sucede cuando N cambia? Suponiendo que el algoritmo requiere un total de "1 operación" y "100 operaciones", la complejidad temporal de ambos casos es constanteO ( 1 ) O(1)O ( 1 ) ; la complejidad temporal de "N operaciones" y "100N operaciones" es linealO ( N ) O(N)O ( N )

representación simbólica

De acuerdo con las características de los datos de entrada, la complejidad del tiempo tiene tres situaciones: "peor", "promedio" y "mejor", respectivamente usando OOOΘ \ ThetaΘΩ \OmegaΩ está representado por tres símbolos. El siguiente es un ejemplo de un algoritmo de búsqueda para ayudar a entender.

Tema : La longitud de entrada es NNUna matriz de enteros de Nnums , determine si hay un número 7 en esta matriz, devuelva si lo haytrue, de lo contrario devuelvafalse.
Ideas para resolver problemas: búsqueda lineal, es decir, atravesar toda la matriz y regresar cuando se encuentre con 7true.
código C:

#include <stdbool.h>

bool find_seven(int* nums, int length) {
   for (int i = 0; i < length; i++) {
      if (nums[i] == 7) {
          return true;
       }
   }
   return false;
}
  • Mejor caso Ω ( 1 ) \Omega(1)Ω ( 1 ) : nums = [7, a, b, c, …] , es decir, cuando el primer número del arreglo es 7, no importa cuántos elementos numshaya
  • Peor caso O ( N ) O(N)O ( N ) : nums = [a, b, c, …] y todos los números numsenNNN veces;
  • Caso promedio Θ \ThetaΘ : es necesario considerar la distribución de los datos de entrada y calcular la complejidad temporal promedio de todos los casos de datos; por ejemplo, en este tema, es necesario considerar la longitud de la matriz, el rango de valores de los elementos de la matriz, etc. .;

grande _O es el símbolo de evaluación de complejidad temporal más utilizado, también conocido como límite superior asintótico, que muestra queNNN aumenta gradualmente la sobrecarga del recurso de tiempoT ( N ) T(N)La tendencia creciente de T ( N ) . De hecho, los resultados del análisis brindan una garantía de que el programa puede finalizar dentro de un cierto período de tiempo. El programa puede terminar temprano, pero nunca tarde.

Reglas de análisis de complejidad de tiempo

El siguiente es un programa para ayudar a comprender el análisis de la complejidad del tiempo, aquí está el cálculo ∑ i = 1 N i 3 \sum\limits_{i = 1}^N { { i^3}}yo = 1norteiUn fragmento de programa simple de 3

int sum(int N){
	int i, PartialSum;

	PartialSum = 0;
	for( i=1; i<=N; i++){
		PartialSum += i * i * i;
	}
	return PartialSum;
}

El análisis de este programa es sencillo. Las declaraciones no cuentan para el tiempo. Las líneas 4 y 8 toman cada una una unidad de tiempo. La línea 6 toma cuatro unidades de tiempo por ejecución (dos multiplicaciones, una suma y una asignación), mientras ejecuta NNN veces ocupan4 N 4N4 N unidades de tiempo. La línea 5 se está inicializandoiiyo , pruebayo ≤ N i\le NiN y pariiHay una sobrecarga implícita en la operación de autoincremento de i . La sobrecarga total de todo esto es 1 unidad de tiempo para inicializar, y todas las pruebasN+1 N+1norte+1 unidad de tiempo y todas las operaciones de autoincrementoNNN unidades de tiempo, un total de2 N + 2 2N+22N _+2 . Ignoramos la sobrecarga de llamar a la función y devolver el valor, y el total es6 N + 4 6N+46 norte+4 , entonces decimos que el programa esO ( N ) O(N)O ( N ) . El análisis se muestra en la siguiente figura:
Ejemplo de análisis de complejidad de tiempo

Si tuviéramos que demostrar todo este trabajo cada vez que analizáramos un programa, la tarea se volvería rápidamente inviable. Afortunadamente, ya que tenemos el gran OOO resultado, por lo que hay muchos atajos que se pueden tomar sin afectar el resultado final. Por ejemplo, la línea 6 (en cada ejecución) es obviamenteO ( 1 ) O(1)O ( 1 ) , por lo que es una tontería calcular exactamente si son dos, tres o cuatro unidades de tiempo; no importa. La línea 4 es obviamente trivial en comparación con el bucle for, por lo que tampoco es aconsejable perder tiempo aquí. Esto nos lleva a varias leyes generales.

  • Regla 1: bucles for :
    el tiempo de ejecución de un bucle for es, como máximo, el tiempo de ejecución de las sentencias (incluidas las pruebas) en el bucle for multiplicado por el número de iteraciones.
  • Regla 2: bucles for anidados :
    analice estos bucles de adentro hacia afuera. El tiempo de ejecución total de una sentencia dentro de un conjunto de bucles anidados es el producto del tiempo de ejecución de la sentencia multiplicado por el tamaño de todos los bucles for del conjunto.
    Como ejemplo, el siguiente fragmento de programa es O ( N 2 ) O(N^2)O ( norte2 ):
    for( i=0; i<N; i++){
    	for( j=0; j<N; j++){
    		k++;
    	}
    }
    
  • Regla 3 - Declaración secuencial :
    tome la declaración de varios segmentos más grande: la complejidad total es igual a la complejidad del código con la magnitud más grande.
    Como ejemplo, el siguiente fragmento de programa usa primero O ( N ) O(N)O ( N ) , costoO ( N 2 ) O(N^2)O ( norte2 ), la sobrecarga total también esO (N 2) O(N^2)O ( norte2 ):
    for( i=0; i<N; i++){
    	A[i] = 0:
    }
    for( i =0; i<N; i++){
    	for( j=0; j<N; j++){
    		A[i] += A[j] + i + j; 
    	}
    }
    
  • Regla 4 - Declaraciones IF/ELSE :
    para fragmentos de programa
    if(Condition){
    	S1;
    }
    else{
    	S2;
    }
    
    El tiempo de ejecución de una instrucción if/ise nunca supera S1el S2tiempo de ejecución total del predicado más el tiempo de ejecución de y , el que sea mayor.

Tipos comunes

La complejidad temporal del algoritmo finalmente expresada debe ser una variable independiente para el tamaño de entrada NNLa función unaria de N , ordenada de menor a mayor, la complejidad temporal de los algoritmos comunes incluye principalmente: O ( 1 ) < O ( log ⁡ N ) < O ( N ) < O ( N log ⁡ N ) < O ( N 2 ) < O ( norte c ) < O ( 2 norte ) < O ( norte ! ) O(1)<O(\log N)<O(N)<O(N\log N)<O(N^2) <O(N^c)<O(2^N)<O(N!)O ( 1 )<O ( lo gnorte )<O ( N )<O ( norteiniciar sesiónnorte )<O ( norte2 )<O ( nortec )<O ( 2norte )<Los niveles O ( N !) exponencial y factorial son catastróficos, y otros niveles son aceptables.
Tipos de complejidad comunes

Análisis de ejemplo

Aquí hay algunos ejemplos de código C de diversa complejidad:

Nivel constante O( 1 ) O(1)O ( 1 ) :
número de carreras frente aNNEl tamaño de N tiene una relación constante, es decir, no varía con el tamaño de los datos de entradaNNN cambia.
Para el siguiente código, independientemente deaaQué tan grande es a , es lo mismo que el tamaño de los datos de entradaNNN es irrelevante, por lo que la complejidad del tiempo sigue siendoO ( 1 ) O(1)O ( 1 )

int algorithm(int N) {
    int count = 0;
    int a = 10000;
    for (int i = 0; i < a; i++) {
        count += 1;
    }
    return count;
}

nivel constante

Clase lineal O ( N ) O(N)O ( N ) :
número de carreras frente aNNEl tamaño de N es lineal y la complejidad temporal esO ( N ) O(N)O ( N ) .
El siguiente código es un bucle de una sola capa, que ejecutaNNN veces, por lo que la responsabilidad del tiempo esO ( N ) O(N)O ( N )

int algorithm(int N) {
    int count = 0;
    for (int i = 0; i < N; i++) {
        count += 1;
    }
    return count;
}

nivel lineal

Clase cuadrada O ( N ) O(N)O ( N ) :
Tome dos capas de bucles como ejemplo, si las dos capas de bucles son independientes entre sí, ambos están relacionados conNNN está relacionado linealmente, por lo que el total yNNN tiene una relación cuadrática y la complejidad temporal esO ( N 2 ) O(N^2)O ( norte2 )
nivel cuadrado

Nivel de polinomio O ( N c ) O(N^c)O ( nortec ):
Entre ellos,ccc es una constante, inteligente, puedes adivinarO ( N 3 ) O (N ^ 3)O ( norte3 )Cómo escribir programas con complejidad temporal.

Exponencial O ( 2 N ) O(2^N)O ( 2N ):
"División celular" en biología es crecimiento exponencial. El estado inicial es 1 celda, 2 después de una ronda de división, 4 después de dos rondas de división, ..., divisiónNNDespués de N rondas hay2 N 2^N2células N.
En el algoritmo, el nivel exponencial a menudo aparece en recursión, y el código del algoritmo y el diagrama esquemático se muestran a continuación.

int algorithm(int N) {
    if (N <= 0) {
        return 1;
    }
    int count_1 = algorithm(N - 1);
    int count_2 = algorithm(N - 1);
    return count_1 + count_2;
}

Exponencial

Logarítmico O ( log ⁡ N ) O(\log N)O ( lo gN ) :
El orden logarítmico es el opuesto al orden exponencial, el orden exponencial es "dividir dos casos por ronda" y el orden logarítmico es "excluir la mitad de los casos por ronda". El orden logarítmico aparece a menudo en algoritmos como "dicotomía" y "divide y vencerás", que encarna la idea algorítmica de "uno se divide en dos" o "uno se divide en muchos".

int algorithm(int N) {
	int count = 1;
	while(count<N){
    	count *= 2;
}

countEl valor inicial es 1, y se multiplica continuamente por 2 para acercarse a NNN , sea el número de ciclosmmm , entonces el tamaño de los datos de entradaes NNN y2 m 2 ^ m2m tiene una relación lineal, tomandolog ⁡ 2 \log_2iniciar sesión2logaritmo, luego obtenga el número de ciclos mmm ylog ⁡ 2 N \log_2Niniciar sesión2N es lineal, es decir, la complejidad temporal esO ( log ⁡ N ) O(\log N)O ( lo gN )
logarítmico

Escala logarítmica lineal O ( N log ⁡ N ) O(N\log N)O ( norteiniciar sesiónN ) :
Las dos capas de bucles son independientes entre sí, y la complejidad temporal de la primera capa y la segunda capa sonO ( log ⁡ N ) O(\log N)O ( lo gN ) yO ( N ) O(N)O ( N ) , la complejidad temporal total esO ( N log ⁡ N ) O(N\log N)O ( norteiniciar sesiónN ) ;

int algorithm(int N) {
    int count = 0;
    int i = N;
    while (i > 1) {
        i = i / 2;
        for (int j = 0; j < N; j++) {
            count += 1;
        }
    }
    return count;
}

El orden logarítmico lineal aparece a menudo en los algoritmos de clasificación, como "clasificación rápida", "clasificación por combinación", "clasificación en montón", etc., y su principio de complejidad temporal se muestra en la siguiente figura.
orden logarítmico lineal

Nivel factorial O ( N ! ) O(N!)O ( N !) :
El nivel factorial corresponde a la "permutación completa" común en matemáticas. Es decir, dadoNNEncuentre todos los arreglos posibles de N elementos que no se repiten, entonces el número de arreglos es: N × ( N − 1 ) × ( N − 2 ) × ⋯ × 2 × 1 = N ! N×(N−1) ×(N −2)×⋯×2×1=N!norte×( norte1 )×( norte2 )××2×1=N ! Como se muestra en la figura y el código a continuación, el factorial a menudo se implementa mediante recursividad. El principio del algoritmo: la primera capa divideNNN , la segunda capa se divide enN − 1 N−1norte1 , ... , hasta elNNTerminar y retroceder en N niveles.

int algorithm(int N) {
    if (N <= 0) {
        return 1;
    }
    int count = 0;
    for (int i = 0; i < N; i++) {
        count += algorithm(N - 1);
    }
    return count;
}

nivel factorial

complejidad del espacio

definición del concepto

Los tipos de espacio involucrados en la complejidad del espacio son:

  • Espacio de entrada : el tamaño del espacio requerido para almacenar los datos de entrada;
  • Espacio de almacenamiento temporal : Durante la operación del algoritmo, el espacio requerido para almacenar todas las variables y objetos intermedios y otros datos;
  • Espacio de salida : cuando el algoritmo regresa de la ejecución, el espacio requerido para almacenar los datos de salida;

Por lo general, la complejidad del espacio se refiere al tamaño de los datos de entrada NNCuando N , el tamaño total del "espacio de almacenamiento temporal" + "espacio de salida" utilizado por el algoritmo para ejecutarse.
complejidad del espacio
Según diferentes fuentes, el espacio de memoria utilizado por el algoritmo se divide en tres categorías:
Espacio de instrucciones:
después de la compilación, el espacio de memoria utilizado por las instrucciones del programa.
Espacio de datos:
el espacio utilizado por varias variables en el algoritmo, incluido: el espacio de memoria utilizado por constantes declaradas, variables, matrices dinámicas y objetos dinámicos.
Espacio de marco de pila:
la función de llamada de programa se implementa en función de la pila.Durante la llamada, la función ocupa un espacio de marco de pila de tamaño constante hasta que se libera después de regresar. Como se muestra en el siguiente código, la función se llama en un bucle y, después de cada ronda de test()llamadaO (1) O(1)O ( 1 )

int test() {
    return 0;
}

void algorithm(int N) {
    for (int i = 0; i < N; i++) {
        test();
    }
}

En el algoritmo, la acumulación de espacio de marcos de pila ocurre a menudo en llamadas recursivas. Como se muestra en el siguiente código, a través de llamadas recursivas, habrá NN al mismo tiempoN funciones no devueltasalgorithm(), uso acumulativo deO ( N ) O(N)Espacio de marco de pila de tamaño O ( N ) .

int algorithm(int N) {
    if (N <= 1) {
        return 1;
    }
    return algorithm(N - 1) + 1;
}

representación simbólica

Por lo general, el algoritmo estadístico de complejidad del espacio usa el tamaño del espacio en el "peor de los casos" para reflejar la cantidad de espacio reservado para que se ejecute el algoritmo, usando el símbolo OOO dijo.
El peor de los casos tiene dos significados, que son "peor dato de entrada" y "peor punto de funcionamiento" en el funcionamiento del algoritmo. Por ejemplo el siguiente código:

Introduzca el número entero NNN , rango de valoresN ≥ 1 N≥1norte1 ;

  • Peores datos de entrada: cuando N ≤ 10 N\le10norteCuando 10 , la longitud numsdeO ( 10 ) = O ( 1 ) O(10)=O(1)O ( 10 )=O ( 1 );当N > 10 N>10norte>Cuando 10 , numsla longitud deNNN , la complejidad del espacio esO ( N ) O(N)O ( N ) ; por lo tanto, la complejidad del espacio debe serO ( N ) O(N)O ( N )
  • Peor punto de ejecución: el algoritmo usa solo O ( 1 ) O(1)int* nums = (int*)malloc(10 * sizeof(int)); cuando se ejecutaO ( 1 ) espacio de tamaño; mientras que nums = (int*)malloc(N * sizeof(int));cuandoO (N) O(N)O ( N ) espacio; por lo tanto, la complejidad del espacio debe serO ( N ) O(N)O ( N )
void algorithm(int N) {
    int num = 5;              // O(1)
    int* nums = (int*)malloc(10 * sizeof(int));  // O(1)
    
    if (N > 10) {
        free(nums);  // 释放原来分配的内存
        nums = (int*)malloc(N * sizeof(int));  // O(N)
    }
}

Tipos comunes

Dispuestas de pequeñas a grandes, las complejidades comunes del espacio de algoritmos son: O ( 1 ) < O ( log N ) < O ( N ) < O ( N 2 ) < O ( 2 N ) O(1)<O(logN) <O (N)<O(N^2)<O(2^N)O ( 1 )<O ( log N ) _ _<O ( N )<O ( norte2 )<O ( 2norte )complejidad común

Análisis de ejemplo

Para todos los siguientes ejemplos, deje que el tamaño de los datos de entrada sea un número entero positivo NNN , clase de nodoNode, funciona test()como

// 节点结构体
struct Node {
    int val;
    struct Node* next;
};

// 创建节点函数
struct Node* createNode(int val) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->val = val;
    newNode->next = NULL;
    return newNode;
}

// 函数 test()
int test() {
    return 0;
}

Nivel constante O( 1 ) O(1)O ( 1 ) :
las constantes ordinarias, las variables, los objetos y las colecciones cuyo número de elementos es independiente del tamaño de los datos de entrada N utilizan un espacio de tamaño constante.

int N = 0;                        // 变量
int num = 0;
int nums[10000] = {0};            // 数组
struct Node* node = createNode(0); // 动态对象

Como se muestra en el siguiente código, aunque la función test()llama a NNN veces, pero después de que test()haO ( 1 ) O(1)O ( 1 )

void algorithm(int N) {
    for (int i = 0; i < N; i++) {
        test();
    }
}

Clase lineal O ( N ) O(N)O ( N ) :
número de elementos vsNNCualquier tipo de colección con una relación lineal entre N (comúnmente encontrada en arreglos unidimensionales, listas enlazadas, tablas hash, etc.), todas usan un espacio de tamaño lineal.

int* nums_1 = (int*)malloc(N * sizeof(int));
int* nums_2 = (int*)malloc((N / 2) * sizeof(int));
struct Node** nodes = (struct Node**)malloc(N * sizeof(struct Node*));

Como se muestra en la figura y el código a continuación, durante esta llamada recursiva, habrá NN al mismo tiempoNalgorithm() funciones que no regresanO ( N ) O(N)Espacio de marco de pila de tamaño O ( N ) .

int algorithm(int N) {
    if (N <= 1) return 1;
    return algorithm(N - 1) + 1;
}

nivel lineal

Nivel cuadrado O ( N 2 ) O(N^2)O ( norte2 ):
Número de elementos yNNCualquier tipo de colección con N al cuadrado (comúnmente visto en matrices), todo usando un espacio de tamaño cuadrado.

int** num_matrix = (int**)malloc(N * sizeof(int*));
struct Node*** node_matrix = (struct Node***)malloc(N * sizeof(struct Node**));

// 初始化 num_matrix 二维数组
for (int i = 0; i < N; i++) {
    num_matrix[i] = (int*)malloc(N * sizeof(int));
    for (int j = 0; j < N; j++) {
        num_matrix[i][j] = 0;
    }
}

// 创建节点对象并初始化
for (int i = 0; i < N; i++) {
    node_matrix[i] = (struct Node**)malloc(N * sizeof(struct Node*));
    for (int j = 0; j < N; j++) {
        node_matrix[i][j] = createNode(j);
    }
}

Como se muestra en la figura y el código a continuación, NN existe al mismo tiempo que las llamadas recursivasNalgorithm() funciones sin retornoO ( N ) O(N)O ( N ) espacio de marcos de pila; los arreglos se declaran en cada capa de función recursiva, con una longitud promedio deN 2 \frac{N}{2}2norte​ , usa O ( N ) O(N)O ( N ) espacio; por lo tanto, la complejidad total del espacio esO ( N 2 ) O(N^2)O ( norte2 )

int algorithm(int N) {
    if (N <= 0) return 0;
    int* nums = (int*)malloc(N * sizeof(int));
    return algorithm(N - 1);
}

nivel cuadrado

Exponencial O ( 2 N ) O(2^N)O ( 2N ):
El orden exponencial es común en árboles binarios y árboles de múltiples bifurcaciones. Por ejemplo, con una altura deNNEl número de nodos en el "árbol binario completo" de N es 2 N 2^N2N , ocupadoO ( 2 N ) O(2^N)O ( 2N )espacio de tamaño; de manera similar, la altura esNNN 'smmEl número de nodos en el " árbol m -ario" esm N m^NmetroN,占用O ( m N ) = O ( 2 N ) O(m^N)=O(2^N)O ( mnorte )=O ( 2N )espacio de tamaño.
Exponencial

Logarítmico O ( log ⁡ N ) O(\log N)O ( lo gN ) :
el orden logarítmico aparece a menudo en la acumulación de espacio de marcos de pila y la conversión de tipo de datos del algoritmo divide y vencerás, por ejemplo:

  • Clasificación rápida , la complejidad del espacio promedio es Θ ( log ⁡ N ) \Theta(\log N)Θ ( lo gN ) , la peor complejidad espacial esO ( N ) O(N)O ( N )
  • Los números se convierten en cadenas y un entero positivo se establece en NNN , entonces la complejidad espacial de la cadena esO ( log ⁡ N ) O(\log N)O ( lo gN ) . La derivación es la siguiente: entero positivoNNEl número de dígitos de N eslog ⁡ 10 ​ N \log_{10}​Niniciar sesión10​N , es decir, la longitud de la cadena convertida eslog ⁡ 10 ​ N \log_{10}​Niniciar sesión10​N , por lo que la complejidad del espacio eslog ⁡ ​ N \log ​Niniciar sesiónN. _

compensación espacio-tiempo

Para el rendimiento del algoritmo, debe evaluarse exhaustivamente a partir del uso del tiempo y el espacio. Un buen algoritmo debe tener dos características, a saber, baja complejidad de tiempo y espacio. De hecho, es muy difícil optimizar simultáneamente la complejidad del tiempo y la complejidad del espacio para un problema de algoritmo. Reducir la complejidad del tiempo a menudo se hace a expensas de aumentar la complejidad del espacio, y viceversa.

Debido a la memoria suficiente de las computadoras contemporáneas, en circunstancias normales, generalmente se adopta el método de "espacio por tiempo" en el diseño de algoritmos, es decir, se sacrifica parte del espacio de almacenamiento de la computadora para aumentar la velocidad de ejecución del algoritmo.

Tomando la primera pregunta de LeetCode, la suma de dos números , como ejemplo, "enumeración violenta" y "tabla hash auxiliar" son dos algoritmos de "espacio óptimo" y "tiempo óptimo" respectivamente.

  • Método 1: Enumeración violenta
    Complejidad temporal O ( N 2 ) O(N^2)O ( norte2 ), la complejidad del espacioes O ( 1 ) O(1)O ( 1 ) ; pertenece a "tiempo por espacio", aunque solo usa un tamaño constante de espacio adicional, funciona demasiado lento.
  • Método 2:
    Complejidad temporal de la tabla hash auxiliar O ( N ) O(N)O ( N ) , complejidad espacialO ( N ) O(N)O ( N ) ; pertenece al "espacio para el tiempo", con la ayuda de la tabla hash auxiliar, al guardar el mapeo de los valores de los elementos de la matriz y los índices para mejorar la eficiencia operativa del algoritmo, es la mejor solución a este problema .

resumen

Lo anterior es el contenido relevante de la complejidad del algoritmo.
El próximo artículo presentará en detalle las nueve estructuras de datos comúnmente utilizadas, y se actualizará continuamente...

Supongo que te gusta

Origin blog.csdn.net/a2360051431/article/details/130736803
Recomendado
Clasificación