[Diario de aprendizaje de algoritmos 01] Clasificaciones de los diez primeros

Tabla de contenido

Referencia: Los diez algoritmos de clasificación clásicos principales
. PD: todos los códigos de este artículo se han verificado mediante 912 preguntas (algunos algoritmos de clasificación expirarán): 912. Clasificación de matrices

1 categoría

Ordenar categorías

2 Complejidad del algoritmo

Método de clasificación complejidad del tiempo complejidad espacial estabilidad
Ordenamiento de burbuja O(n^2) O(1) Estabilizar
clasificación de selección O(n^2) O(1) inestable
tipo de inserción O(n^2) O(1) Estabilizar
clasificación de colinas O(n^1.3) O(1) inestable
Ordenación rápida O (iniciar sesión) O(iniciar sesión~n) inestable
fusionar ordenar O (iniciar sesión) En) Estabilizar
clasificación de montón O (iniciar sesión) O(1) inestable
contando ordenar O(n+k) O(n+k) Estabilizar
ordenar cubos O(n+k) O(n+k) Estabilizar
Ordenación por base O(n*k) O(n+k) Estabilizar

3 algoritmo simple

3.1 Clasificación de burbujas

3.1.1 Descripción del algoritmo

La clasificación de burbujas es un algoritmo de clasificación simple que visita repetidamente la matriz a ordenar, compara dos elementos a la vez y los intercambia si su orden no cumple con los requisitos.

Específicamente, tomando como ejemplo el orden ascendente, el algoritmo de clasificación de burbujas funciona de la siguiente manera:

1. Compara elementos adyacentes. Si el primero es más grande que el segundo, intercámbialos.
2. Haz lo mismo para cada par de elementos adyacentes, comenzando con el primer par y terminando con el último par. Una vez completado este paso, el elemento final será el número máximo.
3. Repita los pasos anteriores para todos los elementos excepto el último elemento hasta que no haya pares de números para comparar.

3.1.2 Demostración GIF

Ordenamiento de burbuja

3.1.3 Implementación del código

void BubbleSort(int[] nums){
    
    
	for(int i=0;i<nums.Length;i++){
    
    
		bool isOver = true; //剪枝:如果不存在一对数顺序不符合要求,则排序完成
        for(int j=0;j<nums.Length-i-1;j++){
    
    
			if(nums[j]>nums[j+1]){
    
    
            	Swap(nums,j,j+1);
	            isOver = false;
            }
        }
        if(isOver) break;
    }
}

3.1.4 Análisis de desempeño

complejidad del tiempo complejidad espacial estabilidad
O (n 2) O(n^2)O ( n.2 ) O(1) O(1)o ( 1 ) Estabilizar

3.2 Clasificación por selección simple

3.2.1 Descripción del algoritmo

La clasificación por selección simple es un algoritmo de clasificación simple e intuitivo. Su principio de funcionamiento es seleccionar el elemento más pequeño / más grande de los elementos de datos que se ordenarán cada vez y colocarlo en la posición inicial de la secuencia. Luego realice los mismos pasos para los elementos restantes sin clasificar.

Específicamente, tomando el orden ascendente como ejemplo, el algoritmo de clasificación por selección simple funciona de la siguiente manera:

1. Inicialmente, la secuencia a ordenar es nums[0…n-1].
2. Seleccione el elemento más pequeño de la secuencia a ordenar e intercámbielo con el bit inicial de la secuencia a ordenar.
3. Excepto el bit inicial, los elementos restantes forman una nueva secuencia para ordenar, repita el paso 2 hasta que la secuencia a ordenar esté vacía (de hecho, puede detenerse cuando solo quede 1 número).

3.2.2 Demostración GIF

clasificación de selección

3.2.3 Implementación del código

void SelectSort(int[] nums){
    
    
	for(int i=0;i<nums.Length-1;i++){
    
    
		int min = i;
		for(int j=i+1;j<nums.Length;j++){
    
    
			if(nums[j]<nums[min]) min = j;
        }
        if(min!=i) Swap(nums,min,i);
    }
}

3.2.4 Análisis de desempeño

complejidad del tiempo complejidad espacial estabilidad
O (n 2) O(n^2)O ( n.2 ) O(1) O(1)o ( 1 ) inestable

3.3 Clasificación por inserción directa

3.3.1 Descripción del algoritmo

La clasificación por inserción directa es un algoritmo de clasificación simple e intuitivo. Su principio de funcionamiento es construir una secuencia ordenada. Para datos no clasificados, escanee de atrás hacia adelante en la secuencia ordenada para encontrar la posición correspondiente e insértela. Se puede imaginar como naipes. .proceso de gestión de tarjetas.

Específicamente, tomando el orden ascendente como ejemplo, el algoritmo de clasificación por inserción directa funciona de la siguiente manera:

1. Trate el primer elemento como la secuencia ordenada inicial.
2. Escanee de atrás hacia adelante comenzando desde el siguiente bit del último elemento en la secuencia ordenada.
3. Si el elemento actual (el elemento en la secuencia ordenada) es más grande que el nuevo elemento, mueva el elemento una posición hacia atrás.
4. Repita el paso 3 hasta que el elemento actual sea menor o igual que el nuevo elemento.
5. Inserte el nuevo elemento después del elemento.
6. Repita los pasos 2 a 5.

3.3.2 Demostración GIF

tipo de inserción

3.3.3 Implementación del código

void InsertSort(int[] nums){
    
    
	for(int i=1;i<nums.Length;i++){
    
    
    	int temp = nums[i];
        int j;
        for(j=i-1;j>=0;j--){
    
    
        	if(nums[j]>temp) nums[j+1] = nums[j];
            else break;
        }
        nums[j+1] = temp;
    }
}

3.3.4 Análisis de desempeño

complejidad del tiempo complejidad espacial estabilidad
O (n 2) O(n^2)O ( n.2 ) O(1) O(1)o ( 1 ) Estabilizar

4 Algoritmo mejorado

4.1 Clasificación de colinas

4.1.1 Descripción del algoritmo

Hill sort es una versión más eficiente y mejorada de la clasificación por inserción. Este método lleva el nombre de DLShell, que fue propuesto en 1959. La clasificación Hill agrupa los registros según un cierto incremento del subíndice y clasifica cada grupo mediante el algoritmo de clasificación por inserción directa; a medida que el incremento disminuye gradualmente, cada grupo contiene más y más palabras clave. Cuando el incremento disminuye a 1, toda la secuencia se divide en un grupo y luego se realiza la clasificación por inserción directa en todos los registros.

La complejidad temporal de la clasificación Hill está relacionada con la selección de la secuencia incremental, y la complejidad temporal promedio es O (n ^ 1,3).

4.1.2 Demostración GIF

clasificación de colinas

4.1.3 Implementación del código

void ShellSort(int[] nums){
    
    
	int gap = 1;
	//PS:关于为什么是gap*3+1而不是gap*3,据说是因为这样比较符合二进制计算机的特点,能够减少乘法运算的次数,从而提高效率;也有说法是说如果不这样做,算法复杂度可能会降到O(n^2)
    while(gap<nums.Length) gap = gap*3+1;
    while(gap>0){
    
    
    	//直接插入排序
        for(int i=gap;i<nums.Length;i++){
    
    
        	int temp = nums[i];
            int j;
            for(j=i-gap;j>=0;j-=gap){
    
    
            	if(nums[j]>temp) nums[j+gap] = nums[j];
                else break;
            }
            nums[j+gap] = temp;
        }
        gap/=3;
    }
}

4.1.4 Análisis de desempeño

complejidad del tiempo complejidad espacial estabilidad
O (norte 1.3) O(norte^{1.3})O ( norte1.3 ) O(1) O(1)o ( 1 ) inestable

4.2 Clasificación rápida

4.2.1 Descripción del algoritmo

La clasificación rápida es un algoritmo de clasificación basado en la idea de dividir y conquistar: divide continuamente la matriz que se va a clasificar en dos submatrices y clasifica las dos partes de forma independiente.

Específicamente, tomando como ejemplo el orden ascendente, la clasificación rápida funciona de la siguiente manera:

1. Seleccione el elemento pivote: seleccione un elemento de la matriz como elemento pivote.
2. Operación de división: coloque todos los elementos más pequeños que el elemento base frente a él y coloque todos los elementos más grandes que el elemento base detrás de él.
3. Ordene rápidamente los subarreglos izquierdo y derecho de forma recursiva.

4.2.2 Demostración GIF

Ordenación rápida

4.2.3 Selección del valor de referencia

Hay tres métodos para seleccionar el valor base para una clasificación rápida:

1. El punto final sirve como valor base.
2. Valor aleatorio como valor base.
3. Encuentra el medio de tres números.

Entre ellos, el método de los tres números es el método de selección de valores de referencia más utilizado. Este método primero selecciona el dígito del medio de los datos en las posiciones izquierda, media y derecha de la matriz para ordenarlos como valor base. Esto puede evitar que la clasificación rápida degenere en clasificación de burbujas cuando la matriz ya está ordenada o casi ordenada.

4.2.4 Implementación del código

1 punto final como valor base
void QuickSort(int[] nums,int left,int right){
    
    
    if(left>=right) return;
    int mid = PartSort(nums,left,right);
    QuickSort(nums,left,mid-1);
    QuickSort(nums,mid+1,right);
}
int PartSort(int[] nums,int left,int right){
    
    
	//最简单的基准值选择:选择待排序数组的第一位元素。
    int pivot = nums[left];
    while(left<right){
    
    
        while(left<right&&nums[right]>=pivot) right--;
        nums[left] = nums[right];
        while(left<right&&nums[left]<=pivot) left++;
        nums[right] = nums[left];
    }
    nums[left] = pivot;
    return left;
}
2 valores aleatorios como valores base
//C# Random实例.Next左闭右开
Random random = new Random();
int index = random.Next(left,right+1);
Swap(nums,index,left);
//Unity Random.Range左闭右开
int index = Random.Range(left,right+1);
Swap(nums,index,left);
3 Método para encontrar el medio de tres números
//在int pivot = nums[left]前加上以下代码,使数值居中的值位于left位。
int mid = (left+right)/2;
if(nums[left]>nums[right]) Swap(nums,left,right);
if(nums[mid]>nums[right]) Swap(nums,mid,right);
if(nums[left]<nums[mid]) Swap(nums,left,mid);

4.2.5 Optimización

Optimización 1: cuando la longitud de la secuencia alcanza un cierto tamaño, utilice la ordenación por inserción.

Cuando la clasificación rápida alcanza una cierta profundidad, el intervalo dividido es muy pequeño y no es eficiente usar la clasificación rápida en este momento, sin embargo, usar la clasificación por inserción en este momento puede evitar algunas situaciones de degradación dañinas.

void QuickSort(int left,int right){
    
    
	if(left>=right) return;
	int len = right-left+1;
	if(len<10) InsertSort(left,right);
	else{
    
    
		int mid = PartSort(left,right);
		QuickSort(left,mid-1);
		QuickSort(mid+1,right);
	}
}

Optimización 2: optimización de la recursión de cola

El algoritmo de clasificación rápida, como la mayoría de los algoritmos de clasificación de divide y vencerás, tiene dos llamadas recursivas. Pero la ordenación rápida es diferente de la ordenación por combinación: la recursividad de la combinación está al comienzo de la función, mientras que la recursividad de la ordenación rápida está al final de la función, lo que permite que el código de ordenación rápida implemente la optimización de la recursividad de cola. Después de utilizar la optimización de la recursión de cola, la profundidad de la pila se puede reducir del O (n) original a O (logn).

Recursión de cola
Si todas las llamadas recursivas en una función ocurren al final de la función, cuando la llamada recursiva es la última declaración ejecutada en todo el cuerpo de la función y su valor de retorno no es parte de la expresión, la llamada recursiva es recursiva de cola.

Cuando el compilador detecta que una llamada a función es recursiva al final, sobrescribe el registro activo actual en lugar de crear uno nuevo en la pila. El compilador puede hacer esto porque la llamada recursiva es la última declaración que se ejecutará en el período activo actual, por lo que cuando la llamada regresa no hay nada más que hacer en el marco de la pila, por lo que no es necesario guardar el marco de la pila. Al sobrescribir el marco de pila actual en lugar de agregar uno nuevo encima, el espacio de pila utilizado se reduce considerablemente, lo que hace que la eficiencia de la operación real sea más eficiente.

un ejemplo

//线性递归
int fact(int n){
    
    
	if(n<0) return 0;
	else if(n==0||n==1) return 1;
	else n*fact(n-1);
}

Cuando n = 5, el proceso recursivo de recursividad lineal es el siguiente:

fact(5)
{
    
    5*fact(4)}
{
    
    5*{
    
    4*fact(3)}}
{
    
    5*{
    
    4*{
    
    3*fact(2)}}}
{
    
    5*{
    
    4*{
    
    3*{
    
    2*fact(1)}}}}
{
    
    5*{
    
    4*{
    
    3*{
    
    2*1}}}}
{
    
    5*{
    
    4*{
    
    3*2}}}
{
    
    5*{
    
    4*6}}
{
    
    5*24}
120
//尾递归
int fact(int n,int a){
    
    
	if(n<0) return 0;
	else if(n==0) return 1;
	else if(n==1) return a;
	else return fact(n-1,a*n);
}

Cuando n = 5, el proceso recursivo de recursividad de cola es el siguiente:

facttail(5,1)
facttail(4,5)
facttail(3,20)
facttail(2,60)
facttail(1,120)
120

Se puede ver que la recursividad de la cola puede reducir muy bien la profundidad de la pila.

Optimización de clasificación rápida
Después de la primera recursión, la variable que queda es inútil, lo que significa que la segunda recursión puede reemplazarse por una estructura de control iterativa.

void QuickSort(int left,int right){
    
    
	if(left>=right) return;
	int len = right-left+1;
	if(len<10) InsertSort(left,right);
	else{
    
    
		while(left<right){
    
    
			int mid = PartSort(left,right);
			QuickSort(left,mid-1);
			left = mid+1;
		}
	}
}

4.2.6 Análisis de desempeño

complejidad del tiempo complejidad espacial estabilidad
O (nlogn) O(nlogn)O ( nlogn ) _ _ _ _ O (logn − n) O(logn-n)O ( iniciar sesión n _norte ) inestable

4.3 Combinar clasificación

4.3.1 Descripción del algoritmo

Merge sort es un algoritmo de clasificación basado en la idea de dividir y conquistar: primero divide una matriz grande en varias matrices pequeñas y luego las fusiona poco a poco. La idea básica de la ordenación por fusión es dividir la secuencia que se va a ordenar en varias subsecuencias, ordenar cada subsecuencia y luego fusionar las subsecuencias en una secuencia ordenada general.

Específicamente, tomando el orden ascendente como ejemplo, el algoritmo de clasificación por combinación funciona de la siguiente manera:

1. Divida la secuencia a ordenar en varias subsecuencias.
2. Fusione subsecuencias adyacentes para obtener varias secuencias ordenadas de longitud 2.
3. Repita el paso 2.

4.3.2 La diferencia entre clasificación rápida y clasificación por combinación

Tanto la clasificación rápida como la clasificación por combinación adoptan la idea de dividir y conquistar, y la complejidad temporal de su algoritmo es O (nlogn), pero los métodos de implementación específicos son diferentes.

La clasificación rápida es un algoritmo de clasificación inestable. Su idea básica es dividir la columna que se va a clasificar en dos partes independientes mediante una clasificación. Las palabras clave registradas en una parte son más pequeñas que las palabras clave de la otra parte, y luego presione esto El método continúa ordenar estas dos partes de registros para lograr el propósito de ordenar toda la secuencia. Además, la clasificación rápida es un algoritmo de clasificación in situ que implementa la clasificación intercambiando elementos en la matriz. La complejidad espacial de la clasificación rápida depende de la profundidad de la pila recursiva. En términos generales, la complejidad espacial es O(logn)~O( norte).

Merge sort es un algoritmo de clasificación estable que requiere espacio adicional para almacenar matrices temporales. La idea básica de la ordenación por fusión es dividir la secuencia que se va a ordenar en varias subsecuencias, ordenar cada subsecuencia y luego fusionar las subsecuencias en una secuencia ordenada general. La complejidad espacial de la ordenación por fusión es O (n).

4.3.3 Demostración GIF

fusionar ordenar

4.3.4 Implementación del código

void MergeSort(int[] nums,int left,int right){
    
    
    if(left>=right) return;
    int mid = (left+right)/2;
    MergeSort(nums,left,mid);
    MergeSort(nums,mid+1,right);
    Merge(nums,left,right);
}
void Merge(int[] nums,int left,int right){
    
    
    int mid = (left+right)/2;
    int i = left;
    int j = mid+1;
    int[] temp = new int[right-left+1];
    int k = 0;
    while(i<=mid&&j<=right){
    
    
        if(nums[i]<=nums[j]) temp[k++] = nums[i++];
        else temp[k++] = nums[j++];
    }
    while(i<=mid) temp[k++] = nums[i++];
    while(j<=right) temp[k++] = nums[j++];
    for(int p=0;p<temp.Length;p++) nums[left+p] = temp[p];
}

4.3.5 Análisis de desempeño

complejidad del tiempo complejidad espacial estabilidad
O (nlogn) O(nlogn)O ( nlogn ) _ _ _ _ O (n) O(n)O ( n ) Estabilizar

4.4 Clasificación del montón

4.4.1 Descripción del algoritmo

La clasificación de montón es un algoritmo de clasificación local que implementa la clasificación manteniendo un montón. El montón es una estructura de datos de árbol especial que cumple las dos condiciones siguientes:

1. El valor de un nodo en el montón siempre no es mayor ni menor que el valor de su nodo principal.
2. El montón es siempre un árbol binario completo.

La idea básica de la clasificación del montón es construir la secuencia que se ordenará en un montón raíz grande o un montón raíz pequeño, y luego eliminar los elementos superiores del montón en secuencia hasta que se ordene toda la secuencia.

Específicamente, tomando como ejemplo el orden ascendente, el algoritmo de clasificación del montón funciona de la siguiente manera:

1. Construya un montón: construya la secuencia inicial de palabras clave que se ordenarán (R1, R2….Rn) en un montón superior grande. En este momento (R1, R2….Rn) es el área desordenada inicial.
2. Intercambio: intercambie el elemento superior R[1] con el último elemento R[n] y obtenga una nueva área desordenada (R1, R2,...Rn-1) y una nueva área ordenada (Rn). satisfacer R[1,2…n-1]<=R[n];
3. Ajuste el montón: dado que el nuevo montón superior R[1] después del intercambio puede violar las propiedades del montón, es necesario ajustar el El área desordenada actual (R1, R2,...Rn-1) se ajusta al nuevo montón, y luego R[1] se intercambia nuevamente con el último elemento del área desordenada para obtener una nueva área desordenada (R1, R2. ..Rn-2) y una nueva área Ordenada (Rn-1,Rn). Este proceso se repite hasta que el número de elementos en el área ordenada sea n-1, luego se completa todo el proceso de clasificación.

4.4.2 Demostración GIF

Insertar descripción de la imagen aquí

4.4.3 Implementación del código

Puntos de conocimiento:

父:i -> 子:2i+1/2i+2
子:i ->:(i-1)/2
void HeapSort(int[] nums){
    
    
    CreateHeap(nums);
    for(int i=nums.Length-1;i>0;i--){
    
    
        Swap(nums,0,i);
        AdjustHeap(nums,0,i-1);
    }
}
void CreateHeap(int[] nums){
    
    
    int last = nums.Length-1;
    for(int i=(last-1)/2;i>=0;i--){
    
    
        AdjustHeap(nums,i,last);
    }
}
void AdjustHeap(int[] nums,int left,int right){
    
    
    int root = left;
    int child = root*2+1;
    while(child<=right){
    
    
        if(child+1<=right&&nums[child]<nums[child+1]) child++;
        if(nums[root]>=nums[child]) return;
        else{
    
    
            Swap(nums,root,child);
            root = child;
            child = root*2+1;
        } 
    }
}

4.4.4 Análisis de desempeño

complejidad del tiempo complejidad espacial estabilidad
O (nlogn) O(nlogn)O ( nlogn ) _ _ _ _ O(1) O(1)o ( 1 ) inestable

5 otros algoritmos

5.1 Clasificación por conteo

5.1.1 Descripción del algoritmo

El núcleo de la clasificación por conteo es convertir los datos de entrada en un subíndice de matriz y almacenarlos en un espacio de matriz abierto adicional, lo que requiere que los datos de entrada sean un número entero positivo con un cierto rango.

Específicamente, tomando como ejemplo el orden ascendente, el algoritmo de ordenación por conteo funciona de la siguiente manera:

1. Encuentre el valor máximo max y el valor mínimo min de la secuencia desordenada y determine el tamaño del espacio de matriz adicional: int[] temp = new int[max-min+1].
2. Recorra la matriz original, cuente el número de apariciones de cada valor en la matriz y regístrelo en la matriz newArr.
3. Mejore la matriz de estadísticas: cada elemento de la matriz de estadísticas es la suma de los recuentos desde newArr hasta el elemento actual.
4. Acolchado inverso.

5.1.2 Demostración GIF

contando ordenar

5.1.3 Implementación del código

void CountSort(int[] nums){
    
    
	//1.找最大最小值以确定额外开辟的数组空间的大小
	int min = nums[0];
	int max = nums[0];
	for(int i=1;i<nums.Length;i++){
    
    
		if(nums[i]<min) min = nums[i];
		if(nums[i]>max) max = nums[i];
	}
	int[] newArr = new int[max-min+1];
	for(int i=0;i<nums.Length;i++){
    
    
		newArr[nums[i]-min]++;
	}
	//2.统计数组-为保证排序稳定
	int[] countArr = new int[newArr.Length];
	for(int i=0;i<newArr.Length;i++){
    
    
		if(i==0) countArr[i] = newArr[i];
		else countArr[i] = newArr[i]+countArr[i-1];
	}
	//3.最终结果
	int[] result = new int[nums.Length];
	for(int i=nums.Length-1;i>=0;i--){
    
    
		result[countArr[nums[i]-min]-1] = nums[i];
		countArr[nums[i]-min]--;
	}
}

5.1.4 Análisis de desempeño

complejidad del tiempo complejidad espacial estabilidad
O ( norte + k ) O (norte+k)O ( n.+k ) O ( norte + k ) O (norte+k)O ( n.+k ) Estabilizar

5.2 Clasificación de cubos

5.2.1 Descripción del algoritmo

La clasificación por cubos es una versión mejorada de la clasificación por conteo. Hace uso de la relación de mapeo de funciones. La clave de la eficiencia radica en la determinación de esta función de mapeo.

El principio de funcionamiento de la clasificación de depósitos: suponga que los datos de entrada obedecen a una distribución uniforme, divida los datos en un número limitado de depósitos y luego clasifique cada depósito por separado (es posible utilizar otros algoritmos de clasificación o continuar usándolos de forma recursiva) clasificación).

Específicamente, tomando el orden ascendente como ejemplo, el algoritmo de clasificación de depósitos funciona de la siguiente manera:

1. Primero encuentre el valor máximo máximo y el valor mínimo mínimo en todos los datos.
2. Determine el tamaño del depósito: determine el rango de datos contenidos en cada depósito según el máximo y el mínimo, tamaño = (máx-mínimo)/n+1, n es la cantidad de datos, debe asegurarse de que haya al menos un depósito, por lo que necesita Agregar 1.
3. Determine la cantidad de depósitos: después de encontrar el tamaño, sabrá el rango de datos contenidos en cada depósito. También debe calcular la cantidad requerida de depósitos cnt, cnt = (max-min)/size+1, y Es necesario garantizar que cada depósito debe poder contener al menos 1 número, por lo que debe agregar 1.
4. Después de obtener el tamaño y el cnt, sabemos que el rango de datos del primer depósito es [min, min+size), el segundo depósito es [min+size, min+2*size),..., y así sucesivamente. .
5. Para ordenar los datos en cada depósito, puede utilizar la clasificación de depósitos de forma recursiva o utilizar otros métodos de clasificación.
6. Genere las secuencias ordenadas en cada depósito en secuencia.

5.2.2 Implementación del código

void BucketSort(int[] nums){
    
    
    //1.找最小值和最大值以计算size和cnt
    int min = nums[0];
    int max = nums[0];
    for(int i=1;i<nums.Length;i++){
    
    
        if(nums[i]<min) min = nums[i];
        if(nums[i]>max) max = nums[i];
    }
    int n = nums.Length;
    int size = (max-min)/n+1;
    int cnt = (max-min)/size+1;
    List<int>[] bucket = new List<int>[cnt];
    for(int i=0;i<cnt;i++){
    
    
        bucket[i] = new List<int>();
    }
    //2.扫描数组,将元素放进对应的桶里
    for(int i=0;i<nums.Length;i++){
    
    
        int index = (nums[i]-min)/size;
        bucket[index].Add(nums[i]);
    }
    //3.对各个桶进行排序
    for(int i=0;i<cnt;i++){
    
    
        bucket[i].Sort();
    }
    //4.反向填充
    int m = 0;
    for(int i=0;i<cnt;i++){
    
    
        for(int j=0;j<bucket[i].Count;j++){
    
    
            nums[m++] = bucket[i][j];
        }
    }
}

5.2.3 Análisis de desempeño

complejidad del tiempo complejidad espacial estabilidad
O ( norte + k ) O (norte+k)O ( n.+k ) O ( norte + k ) O (norte+k)O ( n.+k ) Estabilizar

5.3 Clasificación por base

5.3.1 Descripción del algoritmo

La clasificación Radix es un algoritmo de clasificación de enteros no comparativo. Su principio es cortar el número entero en diferentes números de acuerdo con el número de dígitos y luego comparar cada dígito por separado. El método de clasificación por base puede utilizar LSD (digital menos significativo) o MSD (digital más significativo). El método de clasificación de LSD comienza desde el extremo derecho del valor clave, mientras que el método de clasificación MSD comienza desde el extremo izquierdo del valor clave.

Específicamente, tomando el orden ascendente como ejemplo, el algoritmo de clasificación por base funciona de la siguiente manera:

1. Encuentre el número máximo en la matriz y determine el número de dígitos.
2. Comenzando desde el bit más bajo, cada bit forma una matriz de base.
3. Ordene la matriz de bases por conteo.

5.3.2 Demostración GIF

Ordenación por base

5.3.3 Implementación del código

void RadixSort(int[] nums) {
    
    
	//1.准备桶
    int[,] bucket = new int[10, nums.Length];
    int[] bucketCount = new int[10];
    //2.获取最大数的位数
    int max = nums[0];
    for(int i = 1; i < nums.Length; i++) {
    
    
    	if (nums[i] > max) max = nums[i]; 
    }
    int len = (max + "").Length;
    //3.桶排序
    int arrCount;  //原始数组索引
    for(int k = 0, n = 1; k < len; k++, n *= 10) {
    
    
    	//3.1 第k轮桶排序
        for(int i = 0; i < nums.Length; i++) {
    
    
        	int digit = nums[i] / n % 10;  //获取个十百千万数
            bucket[digit,bucketCount[digit]] = nums[i];
            bucketCount[digit]++;
        }
        //3.2 将桶中数据取出来给原数组
        arrCount = 0;
        for(int i = 0; i < 10; i++) {
    
    
        	if (bucketCount[i] != 0) {
    
    
            	for(int j = 0; j < bucketCount[i]; j++) {
    
    
                	nums[arrCount++] = bucket[i, j];
                }
                }
            }
        //3.3 清空桶
        bucketCount = new int[10];
   }
}

5.3.4 Análisis de desempeño

complejidad del tiempo complejidad espacial estabilidad
O ( n ∗ k ) O(n*k)O ( n.k ) O ( norte + k ) O (norte+k)O ( n.+k ) Estabilizar

6 método de clasificación de listas

6.1 Método de clasificación

list.Sort();      //升序方法
list.Reverse();   //降序方法
list.OrderBy(x=>x.MyProperty); //OrderBy接受一个Lambda表达式指定要按其排序的键

6.2 Reescribir el método de clasificación

6.2.1 x.CompareTo(y)

int result = a.CompareTo(b);

Si a=b, entonces resultado=0; si a>b, entonces resultado=1; si a<b, entonces resultado=-1.

6.2.2 Reescribir

1 comparador personalizado
class CustomComparer : IComparer<MyClass>{
    
    
	public int Compare(MyClass x,MyClass y){
    
    
		return x.MyProperty.CompareTo(y.MyProperty);
	}
}

list.Sort(new CustomComparer());
2 lambdas
list.Sort((x,y)=>x.MyProperty.CompareTo(y.MyProperty));
3 Delegación anónima
list.Sort(delegate(MyClass x,MyClass y){
    
    
	if(x.MyProperty>y.MyProperty) return 1;
	else return -1;
});

7 ejercicios

912 matriz ordenada

Se puede utilizar un poco para practicar la clasificación a mano.

56 intervalo de fusión

public class Solution {
    
    
    public int[][] Merge(int[][] intervals) {
    
    
        //1.依据左端点进行排序
        Array.Sort(intervals,(x,y)=>x[0].CompareTo(y[0]));
        //2.合并重叠区间
        List<int[]> list = new List<int[]>();
        list.Add(intervals[0]);
        for(int i=1;i<intervals.Length;i++){
    
    
            int[] temp = list[list.Count-1];
            if(temp[1]>=intervals[i][0]){
    
    
                temp[1] = Math.Max(temp[1],intervals[i][1]);
            }else{
    
    
                list.Add(intervals[i]);
            }
        }
        return list.ToArray();
    }
}

148 Lista enlazada ordenada

1 Encuentre el punto medio de la lista vinculada: punteros rápidos y lentos

ListNode FindMidNode(ListNode head){
    
    
	if(head==null||head.next==null) return head;
    ListNode fast = head;
    ListNode slow = head;
    while(fast.next!=null){
    
    
    	if(fast.next.next==null) return slow;
        else fast = fast.next.next;
        slow = slow.next;
    }
    return slow;
}

2 Fusionar dos listas enlazadas ordenadas

ListNode Merge(ListNode list1,ListNode list2){
    
    
	if(list1==null) return list2;
    if(list2==null) return list1;
    if(list1.val>list2.val) return MergeTwoLists(list2,list1);
    list1.next = MergeTwoLists(list1.next,list2);
    return list1;
}

3 Resumen

public class Solution {
    
    
    public ListNode SortList(ListNode head) {
    
    
        if(head==null||head.next==null) return head;
        ListNode mid = MiddleNode(head);
        ListNode temp = mid.next;
        mid.next = null;
        ListNode list1 = SortList(head);
        ListNode list2 = SortList(temp);
        return MergeTwoLists(list1,list2);
    }
    //辅助方法1:找到链表的中间结点(当中间结点有两个时,返回前面的那个)
    private ListNode MiddleNode(ListNode head){
    
    
        if(head==null||head.next==null) return head;
        ListNode fast = head;
        ListNode slow = head;
        while(fast.next!=null){
    
    
            if(fast.next.next==null) return slow;
            else fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
    //辅助方法2:合并两个有序链表
    private ListNode MergeTwoLists(ListNode list1,ListNode list2){
    
    
        if(list1==null) return list2;
        if(list2==null) return list1;
        if(list1.val>list2.val) return MergeTwoLists(list2,list1);
        list1.next = MergeTwoLists(list1.next,list2);
        return list1;
    }
}

Supongo que te gusta

Origin blog.csdn.net/manpi/article/details/129856032
Recomendado
Clasificación