[Estructura de datos e implementación del algoritmo C++] 3. Algoritmo de clasificación

El video original es la enseñanza de la estación B de Zuo Chengyun.



Para todas las siguientes funciones swap(), la función se define como

void swap(int& a, int& b)
{
    
    
	int t = a;
	a = b;
	b = t;
}
// 也可以用异或,但不能传入同一个变量,可以是不同变量相同值
void swap(int& a, int& b)
{
    
    
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
}

1 tipo de burbuja O ( N 2 ) O (N ^ 2)O ( norte2 )(independientemente del estado de los datos)

La ordenación más básica y sencilla, nada que decir
Por favor agregue una descripción de la imagen

#incldue <vector>
void bubbleSort(std::vector<int>& arr) 
{
    
    
    for (int i = 0; i < arr.size() - 1; i++) {
    
    
        for (int j = 0; j < arr.size() - 1 - i; j++) {
    
    
            if (arr[j] > arr[j+1]) {
    
            // 相邻元素两两对比
				swap(arr[j], arr[j+1]);
            }
        }
    }
}

2 selección ordenar O ( N 2 ) O (N ^ 2)O ( norte2 )(independientemente del estado de los datos)

La idea básica es seleccionar el elemento más pequeño (o más grande) de los elementos que se ordenarán cada vez y colocarlo al final de la secuencia ordenada. Los pasos principales del ordenamiento por selección son los siguientes:

  • 1. Recorra la secuencia que se va a ordenar y establezca la posición actual como la posición del valor mínimo.
  • 2. Comenzando desde la posición actual, compare el elemento actual con los siguientes elementos, encuentre el elemento más pequeño y registre su posición.
  • 3. Intercambie el elemento más pequeño con el elemento en la posición actual.
  • 4. Repita los pasos 2 y 3 hasta recorrer toda la secuencia.
    Por favor agregue una descripción de la imagen
#include<vector>
void selectionSort(std::vector<int>& arr)
{
    
    
	int len = arr.size();
	if (arr.empty() || len < 2) return;

	for (int i = 0; i < len -1; i++){
    
     // len -1 位置不用再循环了,因为最后剩一个必然是最值
		int minIndex = i;		// 在i ~ len-1 上寻找最小值的下标
		for (int j = i; j < len - 1; j++){
    
    
			minIndex = arr[j] < arr[minIndex] ? j : minIndex;
		}
		swap(arr[minIndex], arr[i]);
	}
}

3 Clasificación por inserción O ( N 2 ) O(N^2)O ( norte2 )(relativo al estado de los datos)

El principio es similar al proceso de clasificar las manos de póquer. Inserte los elementos sin ordenar uno por uno en la parte ordenada en la posición adecuada.

El orden lógico es hacer primero 0~0 ordenado, luego 0~1 ordenado, 0~2 ... 0~n-1 ordenado

El flujo del algoritmo es:

  • 1. A partir del primer elemento, el elemento puede considerarse ordenado.
  • 2. Tome el primer valor de la parte sin ordenar e insértelo en la posición adecuada en la parte ordenada . El método de selección de esta posición es: comparar el valor actual sin ordenar con el valor del vecino izquierdo e intercambiar la posición si es más pequeña . Terminará hasta que encuentre el límite o no sea menor que el valor del vecino izquierdo. (Bucle interior: encuentre una posición adecuada para insertar)
  • 3. Repita el paso 2 hasta que todos los elementos se inserten en la secuencia ordenada. (bucle exterior: recorrer cada número sin ordenar)

A diferencia de la ordenación por burbujas y la ordenación por selección, esta es una operación fija (el estado de los datos es irrelevante). En la clasificación por inserción, si se da el orden, el ciclo externo se completará cada vez, por lo que será O(N) , pero decimos que la complejidad del tiempo es el peor de los casos, por lo que sigue siendo O (N 2) O (N^2)O ( norte2 )

Por favor agregue una descripción de la imagen

#include <vector>
void insertionSort(std::vector<int>& arr)
{
    
    
	int len = arr.size();
	if (arr.empty() || len < 2) return;
	// 0~0已经有序了
	for (int i = 1; i < len; i++){
    
     	// 0~i做到有序
		// 把当前值(j+1)对比左邻值(j),比他小则交换,不满足循环条件则说明找到合适位置了
		for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--){
    
     
			swap(arr[j+1], arr[j]);
		}
	}
}

En el ciclo interno, comparamos el elemento actual arr[j + 1] con su elemento vecino izquierdo arr[j], y transponemos si es más pequeño hasta encontrar una posición de inserción adecuada.
La condición para el final del bucle (encontrar una posición adecuada) es: se alcanza el límite izquierdo o el valor actual es mayor que el valor del vecino izquierdo

Continúe mirando hacia abajo, se recomienda dominar primero la búsqueda binaria y la recursividad básica

4 Clasificación por fusión O ( N log N ) O (NlogN)O ( Nl o g N ) (el estado de los datos no importa)

Su idea central es dividir la secuencia que se ordenará en subsecuencias más pequeñas hasta que cada subsecuencia tenga solo un elemento , y luego fusionar estas subsecuencias en pares hasta que finalmente se ordene la secuencia completa.

Los siguientes son los pasos generales del ordenamiento por fusión:

  • Segmentación : divida la secuencia que se va a clasificar en dos subsecuencias desde la posición media y continúe dividiendo recursivamente cada subsecuencia hasta que solo quede un elemento en cada subsecuencia.
  • Fusionar : fusionar dos subsecuencias ordenadas en una secuencia ordenada. Comience la comparación desde los primeros elementos de las dos subsecuencias, coloque los elementos más pequeños en la matriz temporal y mueva el índice de la subsecuencia correspondiente un bit hacia atrás, hasta que todos los elementos de una de las subsecuencias se coloquen en la matriz temporal. Luego coloque los elementos restantes de la otra subsecuencia directamente en la matriz temporal.
  • Fusión repetida : la operación de fusión se repite hasta que todas las subsecuencias se fusionan en una secuencia ordenada.

Complejidad de tiempo siempre O(NlogN) , independiente de los datos. Aunque es más rápido que el burbujeo, la selección y la clasificación por inserción anteriores, la complejidad del espacio es O(N)

La siguiente figura describe este proceso con mucha precisión.Por favor agregue una descripción de la imagen

#include <vector>
// 第二阶段:merge阶段时,L~M 和 M~R 之间的数必然有序
void merge(std::vector<int>& arr, int L, int M, int R)
{
    
    
	std::vector help(R - L + 1);
	int i = 0;		// 临时数组的起始索引
	int p1 = L;		// 左半部分的起始索引
	int p2 = M + 1;	// 右半部分的起始索引

	//外排序,把两个子数组中的元素从小到大放到help数组
	while (p1 <= M && p2 <= R){
    
     
		help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
	}

 	// 如果左边部分还有剩的,全部依次加到help的末尾
	while (p1 <= M){
    
    
		help[i++] = arr[p1++];
	}
	// 同理如果右边还有剩的
	while (p2 <= R){
    
     
		help[i++] = arr[p2++];
	}

	// 结束,把临时数组的内容覆盖到原数组中
	for (i = 0; i < R - L + 1; i++){
    
    
		arr[L + i] = help[i]; // 注意arr从L位置开始写入的
	}
}

// 务必先看这个函数 这算是第一阶段拆分
void mergeSort(std::vector<int>& arr, int L, int R)
{
    
    
	if (L == R) return;	// 拆分完毕,不能再拆了
	
	int mid = L + ((R - L) >> 1);
	mergeSort(arr, L, mid);
	mergeSort(arr, mid+1, R);
	merge(arr, L, mid, R); // 执行到这一步的条件是 L == mid 且 mid+1 == R 即左右子树都已二分到只有一个元素
}

Complejidad del tiempo (consulte la fórmula maestra 2.1 )

  • T ( norte ) = 2 ⋅ T ( norte 2 ) + O ( norte ) T(N) = 2·T(\frac{N}{2})+O(N)T ( norte )=2 T (2norte)+O ( N )
  • a = 2; b = 2; re = 1
  • logab = 1 = d log_ab=1=dregistro _ _unb=1=d , entonces la complejidad del tiempo esO ( N ⋅ log N ) O(N·logN)O ( norte registro N ) _ _

Complejidad espacial: O ( N ) O(N)O ( N ) . Porque cada vez que se abre una combinación, el tamaño es N

4.1 Extensión de Merge Sort (Encontrar la pequeña suma de matrices)

Tema: En una matriz, la suma de los números a la izquierda de cada número más pequeño que el número actual se denomina suma pequeña de la matriz. Encuentra la suma mínima de una matriz.

Ejemplo: [1,3,4,2,5]
El número a la izquierda de 1 es menor que 1, ninguno; el
número a la izquierda de 3 es menor que 3, 1; el número
a la izquierda de 4 es menor que 4, 1, 3;
el número a la izquierda de 2 es menor que 2 El número, 1;
el número a la izquierda de 5 es menor que 5, 1, 3, 4, 2;
entonces la pequeña suma es 1+1 +3+1+1+3+4+2=16
requiere complejidad de tiempo O(NlogN) , complejidad de espacio O(N)

Si los elementos en cada posición pasan por todos los elementos de la izquierda, encuentra el número más pequeño que él y los suma, el resultado se puede obtener fácilmente y la complejidad del tiempo es O ( N 2 ) O (N ^ 2 )O ( norte2 )

Cambio de pensamiento: en lugar de encontrar cuántos números en el lado izquierdo son más pequeños que el número en la posición actual, y luego sumar, pero contar cuántos números son mayores que el número en la posición actual, ¿cuántas veces debe ser el número actual? acumulado, y este proceso está en Se puede hacer en el tipo externo de tipo de combinación

Explicación detallada del video y precisa en el aire , mucho mejor que leer texto

#include <iostream>
#include <vector>

long long mergeAndCount(std::vector<int>& arr, int left, int mid, int right) 
{
    
    
    std::vector<int> temp(right - left + 1); // 临时数组用于存储合并后的结果
    int i = 0; // 临时数组的起始索引
    int p1 = left; // 左半部分的起始索引
    int p2 = mid + 1; // 右半部分的起始索引
    long long count = 0; // 记录小和的累加和

    while (p1 <= mid && p2 <= right) {
    
    
        if (arr[p1] <= arr[p2]) {
    
    
            // 重点:如果arr[p1]比arr[p2]小,则arr[p1]比p2后面所有数都小!
            count += arr[p1] * (right - p2 + 1);
            temp[i++] = arr[p1++];
        } else {
    
    
            temp[i++] = arr[p2++];
        }
    }

    while (p1 <= mid) temp[i++] = arr[p1++];
    while (p2 <= right) temp[i++] = arr[p2++];

    // 将临时数组的结果复制回原数组
    for (i = 0; i < R - L + 1; i++) {
    
    
        arr[left + i] = temp[i];
    }

    return count;
}

long long mergeSortAndCount(std::vector<int>& arr, int left, int right) 
{
    
    
    if (left >= right) {
    
    
        return 0; // 单个元素无小和
    }

    int mid = left + (right - left) / 2;
    long long leftCount = mergeSortAndCount(arr, left, mid); // 左半部分的小和
    long long rightCount = mergeSortAndCount(arr, mid + 1, right); // 右半部分的小和
    long long mergeCount = mergeAndCount(arr, left, mid, right); // 合并过程中的小和

    return leftCount + rightCount + mergeCount; // 总的小和
}

long long calculateSmallSum(std::vector<int>& arr) 
{
    
    
    if (arr.empty()) return 0; // 空数组无小和

    int left = 0;
    int right = arr.size() - 1;
    return mergeSortAndCount(arr, left, right);
}

int main() 
{
    
    
    std::vector<int> arr = {
    
    3, 1, 4, 2, 5};
    long long smallSum = calculateSmallSum(arr);
    std::cout << "Small Sum: " << smallSum << std::endl;
	
	std::cin.get();
    return 0;
}

5 Clasificación rápida

Quicksort 3.0 (ver en otro lugar para 1.0 y 2.0)

Supongo que te gusta

Origin blog.csdn.net/Motarookie/article/details/131383596
Recomendado
Clasificación