Notas de estudio del algoritmo --- buscar

Directorio de artículos

1. Algoritmo y análisis de algoritmos

Que es un algoritmo:

Una descripción de los pasos de solución para un problema particular, que es una secuencia finita de instrucciones, donde cada instrucción representa una o más operaciones.

Cinco propiedades importantes del algoritmo:

  • finitud
  • Certeza
  • factibilidad
  • ingresar
  • producción

Requisitos de diseño del algoritmo:

  • exactitud
  • legibilidad
  • robustez
  • Alta eficiencia y bajo almacenamiento

Una medida de la eficiencia algorítmica:

El tiempo de ejecución del algoritmo debe medirse de acuerdo con el tiempo que consume el programa compilado por el algoritmo para ejecutarse en la computadora. Generalmente, existen dos métodos:

El método de las estadísticas a posteriori:

​ Muchas computadoras tienen una función de cronometraje en su interior, los programas de diferentes algoritmos pueden distinguir los pros y los contras a través de un conjunto o varios de los mismos datos estadísticos, por ejemplo: time ./a.out, pero este método tiene dos defectos. primero debe ejecutar el programa programado de acuerdo con el algoritmo. Si el programa no cumple con los requisitos, la mano de obra y los recursos materiales gastados en escribir el programa se desperdiciarán. En segundo lugar, las estadísticas del tiempo obtenido dependen de factores ambientales como la computadora hardware y software, y a veces es fácil encubrir las ventajas y desventajas del propio algoritmo, por lo que las personas suelen utilizar otro método de análisis ex ante.

Métodos de análisis previo y estimación:

El tiempo que tarda un programa escrito en un lenguaje de programación de nivel de usuario en ejecutarse en una computadora depende de los siguientes factores:

1. De acuerdo con la estrategia que se use primero de acuerdo con el algoritmo, como la operación de bucle, puede usar recursión de función o instrucciones de bucle como for, while y do while.

2. La escala del problema, resolviendo problemas con una pequeña cantidad de datos, resolviendo problemas a gran escala y resolviendo problemas con datos masivos.

3. El lenguaje utilizado para escribir programas, para el mismo algoritmo, cuanto mayor sea el nivel de lenguaje implementado, menor será la eficiencia de ejecución.

4. La calidad del código máquina generado por el programa compilado.

5. La velocidad a la que la máquina ejecuta las instrucciones, el mismo lenguaje de programación, el compilador utilizado es diferente, las instrucciones de la máquina generadas serán diferentes y la velocidad de ejecución será diferente.

Resumen: El mismo algoritmo se implementa en diferentes lenguajes, o se compila con diferentes compiladores, o se ejecuta en diferentes computadoras, la eficiencia no es la misma, lo que demuestra que no es adecuado utilizar unidades de tiempo absoluto para medir la eficiencia del algoritmo. En estos factores relacionados con el hardware y el software de la computadora, se puede considerar que la "carga de trabajo en ejecución" de un algoritmo específico depende solo de la escala del problema, o en otras palabras, es una función de la escala del problema.

Un algoritmo se compone de estructuras de control (tres estructuras básicas, como secuencia, rama y bucle) y operaciones originales (referidas a operaciones de tipos de datos inherentes), y el tiempo del algoritmo depende del efecto de integración de los dos. facilitar la comparación del mismo problema Los diferentes algoritmos de diferentes algoritmos, el enfoque de los estudiantes es seleccionar una operación original del algoritmo que sea una operación básica para el problema en estudio, y usar el número de veces que se ejecuta repetidamente la operación básica como la medida de tiempo del algoritmo.

​ Por lo tanto: el número de ejecuciones repetidas de operaciones básicas en el algoritmo es una determinada función f(n) del tamaño del problema n, y la medida del tiempo del algoritmo se describe como T(n)=O(f(n)) , que representa el aumento del tamaño del problema n , la tasa de crecimiento del tiempo de ejecución del algoritmo es la misma que la tasa de crecimiento de f(n), que se denomina complejidad temporal asintótica del algoritmo o complejidad temporal para abreviar.

Las complejidades de tiempo comunes son:

  • O(1)
  • O(registro2N)/O(registroN)
  • EN)
  • O(NlogN)
  • O(N^2)
  • O(2^N)

Nota: La complejidad temporal general es una situación aproximada que no será lo suficientemente precisa para ser particularmente detallada, es decir, no será 100% correcta.

Peor tiempo de complejidad:

El orden de clasificación extremo de los datos hará que el algoritmo funcione peor.

Complejidad de tiempo óptima:

Una situación en la que el orden de clasificación extremo de los datos hará que el algoritmo funcione de manera óptima.

Complejidad de tiempo promedio:

El orden de clasificación de los datos es la ley de desarrollo del equipo, y la eficiencia de ejecución del algoritmo está cerca de la situación real.

Complejidad del espacio:

Representa una determinada función f(n) con el tamaño del problema n, que representa el aumento del tamaño del problema n, y la tasa de crecimiento del espacio de almacenamiento requerido por el algoritmo, que se denomina complejidad espacial del algoritmo.

2. Algoritmo de búsqueda

¿Qué es el algoritmo de búsqueda?

​ En una secuencia de datos, encontrar si existe un dato determinado o dónde existe se usa con mucha frecuencia en el proceso de desarrollo real. Por ejemplo, las operaciones comunes en datos incluyen agregar, eliminar, modificar y verificar. Al agregar datos, es necesario para consultar nuevos Si los datos agregados están duplicados, al eliminar datos, primero debe consultar la ubicación de los datos antes de eliminarlos, y al modificar los datos, también debe consultar primero la ubicación de los datos modificados. El algoritmo de búsqueda ocupa el primer lugar en importancia en la programación.

Búsqueda secuencial:

1) En el mejor de los casos: lo primero que debe buscar es . La complejidad temporal es: O(1)
(2) Peor caso: el último es el elemento a encontrar. Complejidad temporal: O(n)
(3) En promedio, es: (n+1)/2.

Búsqueda secuencial para tablas secuenciales:
// 在顺序表中按照从前到后的顺序查找数据,如果找到则返回合法的下标,找不到则返回 -1
int linear_search(int* arr,size_t len,int key)
{
    
    
	for(int i=0; i<len; i++)
    {
    
    
        if(arr[i] == key)
            return i;
    }
    return -1;
}
Búsqueda secuencial de lista enlazada:
// 在顺序表中按照从前到后的顺序查找数据,如果找到则返回所在节点的地址,找不到则返回NULL
Node* linear_search(Node* head,int key)
{
    
    
	for(Node* n=head; NULL!=n; n=n->next)
    {
    
    
        if(n->data == key)
            return n;
    }
    return NULL;
}
Ventajas de la búsqueda secuencial:

No hay requisitos para el orden de los datos, y se pueden buscar sin importar si los datos están ordenados o no.

No hay ningún requisito sobre la estructura de datos, ya sea una lista secuencial o una lista vinculada, se puede buscar.

Desventajas de la búsqueda secuencial:

La velocidad de búsqueda es más lenta que otros algoritmos de búsqueda, la complejidad de tiempo óptima: O(1), el peor grado de reintento de tiempo: O(N), la complejidad de tiempo promedio: O(N)

Búsqueda binaria:

​ La secuencia de datos debe estar en orden, y luego la clave de la palabra clave se compara con los datos intermedios, y si son iguales, se devolverá de inmediato.Si la clave es más pequeña que los datos intermedios, solo necesita continuar buscando a la izquierda de los datos intermedios. Si la clave es mayor que los datos intermedios, solo necesita estar en Simplemente continúe buscando en el lado derecho de los datos intermedios, y repita este paso hasta encontrar la palabra clave, o si la izquierda y los lados derechos de los datos del medio están vacíos, la búsqueda falla.

// 循环语句实现
int binary_search(int* arr, size_t len, int key)
{
    
    
    int left = 0 , right = len;
    while(left < right)
    {
    
    
        int p = (left + right) / 2;
        if(arr[p] == key)
            return p;
        if(arr[p] > key)
            r = p;
        if(arr[p] < key)
            l = p+1;
    }
    return -1
}

// 函数递归实现
int _binary_search(int* arr, int left, int right, int key)
{
    
    
 	if(left >= right)
        return -1;
 	int p = (left + right) / 2;
    if(arr[p] > key)
        return _binary_search(arr,left,p,key);
    if(arr[p] < key)
        return _binary_search(arr,p+1,right,key);
    return p;
}

int binary_search(int* arr, size_t len, int key)
{
    
    
    return _binary_search(arr,0,len,key);
}
Ventajas de la búsqueda binaria:

Velocidad de consulta rápida, complejidad de tiempo: O (log2N).

Desventajas de la búsqueda binaria:

1. Hay requisitos para el orden de los datos, que deben ordenarse primero.

2. Tiene requisitos en la estructura de datos y no es adecuado para listas enlazadas, porque los datos en la posición media no se pueden calcular.

Búsqueda de índice:

La búsqueda de índice es una búsqueda en la tabla de índice y la tabla principal (es decir, la estructura de almacenamiento de índice de la tabla lineal), pero primero se debe establecer la tabla de índice. La tabla de índice es similar al catálogo de libros, que puede mejorar en gran medida la eficiencia de la búsqueda.

Cree una tabla de índice para la tabla de secuencia:
typedef struct Student
{
    
    
    int id; 
    char name[20];
    char sex;
    short age;
    float src;
}Student;

Student stu[1000];

typedef struct Index
{
    
    
    int id; 
    void* addr;
}Index;

Index* index[1000];
void create_index(Student* stu,Index* index,size_t len)
{
    
    
    for(int i=0; i<len; i++)
    {
    
       
        index[i].id = stu[i].id;
        index[i].addr = stu+i; 
    }   
}
Búsqueda secuencial de una tabla indexada:
// 它比直接查询原感受的跳转速度快,如果数据在内存中可以减少跳转的内存页数量,如果数据在机械硬盘中可以减少硬盘磁头的移动距离,如果使用stu[i],i每增加一下,需要跳转sizeof(Student)个字节,如果使用index[i],i每增加一下,只需要跳转sizeof(Index)个字节,。
int linear_index(Index* index,size_t len,int id)
{
    
    
    for(int i=0; i<len; i++)
    {
    
    
        if(index[i].id == id)
            return i;
    }
}
Búsqueda binaria de tabla de índice:
// 如果对原表直接排序,则每次需要交换sizeof(Student),如果对索引表进行排序每次只需要交换sizeof(Index)个字节,数据的交换量对排序算法的程度影响非常大。
void sort_index(Index* index,size_t len)
{
    
    
    for(int i=0; i<len-1; i++)
    {
    
       
        int min = i;
        for(int j=i+1; j<len; j++)
        {
    
    
            if(index[min].id > index[j].id)
                min = j;
        }

        if(i != min)
        {
    
    
            Index tmp = index[min];
            index[min] = index[i];
            index[i] = tmp;
        }
    }
}

Student* binary_search(Index* index,size_t len,int id)
{
    
    
    int left = 0, right = len;
    while(left < right)
    {
    
    
        int p = (left + right) / 2;
        if(index[p].id == id)
            return index[p].ptr;
        if(index[p].id > id)
            right = p;
        if(index[p].id < id)
            left = p+1;
    }
    return NULL;
}
Cree una tabla de índice para la lista vinculada:
// 给链表创建了索引表后,再对索引表进行排序,就可以对链表进行二分查找的效果
void create_index(Node* head,Index* index)
{
    
    
    int i = 0;
    for(Node* n=head; NULL!=n; n=n->next)
    {
    
    
        index[i].id = ((Student*)n->ptr)->id;
        index[i].addr = n->ptr;
    }
}
Ventajas de las búsquedas indexadas:

1. Para la búsqueda secuencial de la tabla secuencial, la tabla de índice puede reducir el rango de búsqueda de los datos.

2. Para la búsqueda binaria de la tabla de secuencias, la tabla de índices puede mejorar la velocidad de clasificación.

3. Para la lista enlazada, la búsqueda binaria se puede realizar después de crear la tabla de índice.

Desventajas de las búsquedas indexadas:

El uso de espacio de almacenamiento adicional para crear tablas de índice es una estrategia típica de intercambio de espacio por tiempo.

Casos de uso para la búsqueda de índice:

Para los datos de tablas secuenciales en la memoria, la velocidad de búsqueda de índices no mejora particularmente, pero la velocidad de búsqueda de datos almacenados en discos duros mecánicos mejora mucho, por lo que la búsqueda de índices se usa mucho en las bases de datos.

Búsqueda de bloque:

Entre O(log2n) y O(n)

Primero divida los datos en bloques, que se pueden dividir en bloques según la fecha.Los datos de plástico se pueden dividir en 10 subtablas según el último dígito de los datos, y los datos de cadena se pueden dividir en 26 sub- tablas según la primera letra, y luego realizar una búsqueda binaria y una búsqueda secuencial en estas subtablas, que es la realización concreta de la idea de divide y vencerás.

Ventajas de la búsqueda de bloques:

Los datos masivos se pueden dividir para reducir el tamaño de los datos, lo que aumenta la velocidad de búsqueda.

Desventajas de la búsqueda de bloques:

La operación es más problemática y puede haber una cierta cantidad de desperdicio de memoria.

Árbol ordenado binario y árbol binario equilibrado:

​ Un árbol de clasificación binaria, también llamado árbol de búsqueda binaria, se construye de acuerdo con el valor de los datos, y luego realiza una búsqueda transversal previa al pedido. Su principio de búsqueda sigue siendo una búsqueda binaria. Lo he detallado en la búsqueda binaria. árbol, así que no lo repetiré aquí.

Un árbol binario balanceado es primero un árbol de clasificación binaria o un árbol de búsqueda binaria.Cuando se crea un árbol de clasificación binaria, debido a que los datos están básicamente ordenados, el árbol de clasificación binaria creado tendrá una sola rama o se insertarán datos aleatorios. , la eliminación hace que el desequilibrio izquierdo y derecho del árbol de clasificación binario sea de una sola rama, lo que hará que la velocidad de consulta de la clasificación del árbol binario esté cerca de la O(N) de la lista enlazada unidireccional;

​ Un árbol binario equilibrado requiere que la diferencia de altura entre los subárboles izquierdo y derecho no exceda de 1, lo que hará que los lados izquierdo y derecho del árbol de clasificación binario sean iguales, y hará que la velocidad de búsqueda del árbol de clasificación binario sea cercana a la búsqueda binaria (para comprender la diferencia entre el árbol AVL y el árbol rojo-negro).

Búsqueda de tabla hash:

O(1)

Al buscar datos, se requieren una serie de operaciones de comparación. Ya sea búsqueda secuencial, búsqueda binaria, búsqueda de índice, dichos algoritmos de búsqueda se basan en la comparación, pero el algoritmo de búsqueda más ideal no pasa por ninguna comparación, los datos pueden se encuentra en un acceso, entonces es necesario establecer una correspondencia definida entre la ubicación de almacenamiento y sus palabras clave, de modo que cada palabra clave corresponda a una ubicación de almacenamiento única en los datos. Al buscar, los datos correspondientes a la palabra clave dada se encuentran de acuerdo con la relación correspondiente, de modo que los datos buscados se pueden obtener directamente sin comparación.

Llamamos función hash a la correspondencia entre ubicaciones de almacenamiento y palabras clave, y una tabla construida de acuerdo con esta idea es una tabla hash.

Pregunta: Suponga que hay 1 millón de números aleatorios entre 0 y 255, y luego ingrese un número entero entre 0 y 255 para averiguar cuántas veces aparece el número en total.

#include <stdio.h>
#include <stdlib.h>

int arr[1000000];
int cnts[256];
// 最简单的哈希表
int main(int argc,const char* argv[])
{
    
    
    for(int i=0; i<1000000; i++)
    {
    
    
        arr[i] = rand() % 256;
    }

    // 把数据会值作为数组cnts的下标(把要关键字与存储位置相对应),直接定值法,H(key)=key
    for(int i=0; i<1000000; i++)
    {
    
    
        cnts[arr[i]]++;
    }

    unsigned char num;
    printf("请输入一个整数(0~255):");
    scanf("%hhu",&num);

    printf("%d\n",cnts[num]);
    return 0;
}

​ El ejemplo anterior tiene grandes limitaciones. En muchos casos, el valor de los datos no se puede usar como el subíndice de la matriz, porque la cantidad de datos puede ser pequeña, pero el rango de diferencia es demasiado grande, lo que hará que la tabla hash para ser demasiado grande y no práctico, por lo que es necesario procesar el valor de la palabra clave. El método de procesamiento es la función hash, y luego se reduce la tabla hash. Sin embargo, los resultados calculados por el diseño de la función hash pueden ser repetido.Por lo tanto, una función hash adecuada está diseñada para resolver conflictos.El problema es la clave para la tabla hash y la búsqueda hash, y muchas veces es imposible diseñar una función hash, como datos de punto flotante, cadenas de caracteres chinos, etc. Es difícil diseñar una función hash adecuada.

El método de diseño de la función hash:
Método de valoración directa:

Tome el valor de la clave o una función lineal como la dirección k-hash: H(clave) = clave o H(clave)=a*clave-b, pero este método tiene altos requisitos de datos.

Análisis digitales:

Suponiendo que la clave de la palabra clave se usa como base y se conocen todos los números que pueden aparecer en la tabla hash, algunos dígitos de la palabra clave se usan como la dirección hash, como el número de estudiante:

​ 1 2 departamentos, 3 4 son especializaciones, 5 6 son grados, 7 9 son números de admisión, entonces H(clave)=clave%1000.

Los dos métodos anteriores tienen limitaciones relativamente grandes, pero la dirección hash calculada no tiene posibilidad de conflicto.Los siguientes métodos: el método de tomar la mitad del cuadrado, el método de plegado y el método de eliminación del resto no requieren palabras clave, pero el método calculado dirección hash Las direcciones pueden entrar en conflicto.

Maneras de resolver colisiones de valores hash:
  • método de la raíz cuadrada
  • refrito
  • método de dirección de cadena
  • Crear un área de desbordamiento común
Ventajas de la búsqueda de hash:

La velocidad de búsqueda es extremadamente rápida y la complejidad del tiempo puede llegar a O (1).

Desventajas de la búsqueda de hash:

1. Las limitaciones son relativamente grandes y los requisitos de datos son relativamente altos.

2. Diseñar una función hash es problemático No existe un método específico para diseñar una función hash, pero hay algunas ideas generales.

3. Es necesario establecer una tabla hash, que ocupa espacio adicional.

Road, tome ciertos dígitos de la palabra clave como la dirección hash, como el número de estudiante:

​ 1 2 departamentos, 3 4 son especializaciones, 5 6 son grados, 7 9 son números de admisión, entonces H(clave)=clave%1000.

Los dos métodos anteriores tienen limitaciones relativamente grandes, pero la dirección hash calculada no tiene posibilidad de conflicto.Los siguientes métodos: el método de tomar la mitad del cuadrado, el método de plegado y el método de eliminación del resto no requieren palabras clave, pero el método calculado dirección hash Las direcciones pueden entrar en conflicto.

Maneras de resolver colisiones de valores hash:
  • método de la raíz cuadrada
  • refrito
  • método de dirección de cadena
  • Crear un área de desbordamiento común
Ventajas de la búsqueda de hash:

La velocidad de búsqueda es extremadamente rápida y la complejidad del tiempo puede llegar a O (1).

Desventajas de la búsqueda de hash:

1. Las limitaciones son relativamente grandes y los requisitos de datos son relativamente altos.

2. Diseñar una función hash es problemático No existe un método específico para diseñar una función hash, pero hay algunas ideas generales.

3. Es necesario establecer una tabla hash, que ocupa espacio adicional.

Supongo que te gusta

Origin blog.csdn.net/m0_62480610/article/details/126656386
Recomendado
Clasificación