Notas de estudio de algoritmos y estructura de datos de Javascript (abajo - algoritmo de clasificación)

1. Notación O grande

Notación O grande:

  • El uso de medidas aproximadas en las computadoras para describir la eficiencia de los algoritmos informáticos se conoce como notación "O grande".
  • Cuando cambia el número de elementos de datos , la eficiencia del algoritmo también cambiará. Por lo tanto, no tiene sentido decir que el algoritmo A es el doble de rápido que el algoritmo B.
  • Por lo tanto, generalmente usamos cómo cambiará la velocidad del algoritmo con el cambio de la cantidad de datos para expresar la eficiencia del algoritmo, y la notación O grande es una de las formas.

Notación O grande común

 Complejidad temporal de diferentes formas de O grande:

 Se puede ver que la eficiencia de grande a pequeña es: O(1) > O(logn) > O(n) > O(nlog(n)) > O(n²) > O(2n)

Tres reglas para derivar la notación Big O:

  • Regla 1 : Reemplace todas las constantes aditivas en tiempo de ejecución con la constante 1. Por ejemplo, 7 + 8 = 15, use 1 para representar el resultado de la operación 15, y la notación O grande se expresa como O(1);
  • Regla 2 : En la operación sólo se mantiene el término de mayor orden. Como N^3 + 3n +1, la notación O grande se expresa como: O(N3);
  • Regla 3 : Si la constante del término de mayor orden no es 1, se puede omitir. Como 4N2, la notación O grande se expresa como: O(N2);

2. Algoritmo de clasificación

Aquí hay varias clasificaciones simples y clasificaciones avanzadas:

  • Clasificación simple: clasificación por burbuja, clasificación por selección, clasificación por inserción;
  • Clasificación avanzada: clasificación Hill, clasificación rápida;

Cree una clase de lista ArrayList aquí y agregue algunas propiedades y métodos para almacenar estos métodos de clasificación:

    //创建列表类
    function ArrayList() {
      //属性
      this.array = []

      //方法
      //封装将数据插入到数组中方法
      ArrayList.prototype.insert = function(item){
        this.array.push(item)
      }

      //toString方法
      ArrayList.prototype.toString = function(){
        return this.array.join('-')
      }

      //交换两个位置的数据
      ArrayList.prototype.swap = function(m, n){
        let temp  = this.array[m]
        this.array[m] = this.array[n]
        this.array[n] = temp
      }

1. Clasificación de burbujas

Idea de clasificación de burbujas:

  • Para cada elemento sin clasificar, compare la relación de tamaño de los dos elementos adyacentes desde el principio hasta el final ;
  • Si el elemento de la izquierda es más grande , se intercambiarán las posiciones . Por ejemplo, 1 es menor que 2, no intercambies posiciones;
  • Muévase un bit a la derecha , continúe comparando 2 y 3, y finalmente compare los dos datos de longitud - 1 y longitud - 2;
  • Al llegar al extremo derecho , la persona más alta debe colocarse en el extremo derecho ;
  • De acuerdo con esta idea, al reiniciar desde el extremo izquierdo, solo necesita ir a la penúltima posición ;

Idea de implementación:

Dos bucles:

  • El bucle exterior controla el número de tiempos de burbujeo:

    • La primera vez: j = longitud - 1, en comparación con la primera última posición;
    • La segunda vez: j = longitud - 2, en comparación con la penúltima posición;
  • El bucle interno controla el número de comparaciones por pase:

    • La primera comparación: i = 0, compara dos datos en las posiciones 0 y 1;
    • La última comparación: i = longitud - 2, comparar longitud - 2 y longitud - 1 dos datos;

El proceso detallado se muestra en la siguiente figura:

 Código:

      //冒泡排序
      ArrayList.prototype.bubblesor = function(){
        //1.获取数组的长度
        let length = this.array.length

        //外层循环控制冒泡趟数
        for(let j = length - 1; j >= 0; j--){
          //内层循环控制每趟比较的次数
          for(let i = 0; i < j; i++){
          if (this.array[i] > this.array[i+1]) {
            //交换两个数据
            let temp  = this.array[i]
        	this.array[i] = this.array[i+1]
        	this.array[i+1] = temp
          }
        }
        }
      }

Eficiencia del tipo de burbuja:

  • Para los 7 elementos de datos mencionados anteriormente, el número de comparaciones es: 6 + 5 + 4 + 3 + 2 + 1;
  • Para N elementos de datos, el número de comparaciones es: (N - 1) + (N - 2) + (N - 3) + ... + 1 = N * (N - 1) / 2; si se intercambian dos comparaciones una vez, entonces el número de intercambios es: N * (N - 1) / 4;
  • Utilice la notación O grande para representar el número de comparaciones y el número de intercambios respectivamente: O( N * (N - 1) / 2) y O( N * (N - 1) / 4), según las tres reglas de la notación O grande Abreviatura de: O(N^2) ;

2. Clasificación de selección

La ordenación por selección mejora la ordenación por burbujas:

  • Reducir el número de intercambios de O(N^2) a O(N) ;
  • Pero el número de comparaciones sigue siendo O(N^2) ;

La idea del tipo de selección:

  • Seleccione la posición del primer índice, como 1, y luego compárelo con los siguientes elementos a su vez ;
  • Si el siguiente elemento es menor que el elemento en el índice 1, cambie la posición al índice 1;
  • Después de una ronda de comparación, se puede determinar que el elemento en el índice 1 especificado al principio es el más pequeño ;
  • Luego use el mismo método para comparar los elementos restantes uno por uno excepto el índice 1 ;
  • Se puede ver que en la clasificación por selección, el valor mínimo se seleccionará en la primera ronda , y el segundo valor más pequeño se seleccionará en la segunda ronda hasta que se complete la clasificación.

Idea de implementación:

Dos bucles:

  • El bucle exterior controla el índice especificado:

    • La primera vez: j = 0, especificando el primer elemento;
    • Última vez: j = longitud - 1, especifique el último elemento;
  • El ciclo interno es responsable de comparar el elemento en el índice especificado (i) con el elemento restante (i - 1);

      //选择排序
      ArrayList.prototype.selectionSort = function(){
        //1.获取数组的长度
        let length = this.array.length

        //2.外层循环:从0开始获取元素
        for(let j = 0; j < length - 1; j++){
          let min = j
          //内层循环:从i+1位置开始,和后面的元素进行比较
        for(let i = min + 1; i < length; i++){
          if (this.array[min] > this.array[i]) {
            min = i
          }
        }
        this.swap(min, j)
        }
      }

Eficiencia del tipo de selección:

  • El número de comparaciones para la clasificación por selección es: N * (N - 1) / 2, expresado en notación O grande como: O(N^2) ;
  • El número de intercambios para la clasificación por selección es: (N - 1) / 2, expresado en notación O grande como: O(N) ;
  • Por lo tanto, la eficiencia de la clasificación por selección es mayor que la de la clasificación por burbujas;

3. Clasificación por inserción

La ordenación por inserción es la ordenación más eficiente en la ordenación simple .

La idea del tipo de inserción:

  • El núcleo de la idea de clasificación por inserción es el orden local . Como se muestra en la figura, la persona a la izquierda de X se llama orden local ;
  • Primero especifique un dato X (a partir del primer dato) y cambie el lado izquierdo del dato X a un estado ordenado local;
  • Luego mueva X un bit a la derecha, y después de llegar nuevamente al orden local, continúe moviéndose un bit a la derecha y repita la operación anterior hasta que X se mueva al último elemento.

      //插入排序
      ArrayList.prototype.insertionSort = function(){
        //1.获取数组的长度
        let length = this.array.length

        //2.外层循环:从第二个数据开始,向左边的已经局部有序数据进行插入
        for(let i = 1; i < length; i++){
          //3.内层循环:获取i位置的元素,使用while循环(重点)与左边的局部有序数据依次进行比较
          let temp = this.array[i]
          let j = i
          while(this.array[j - 1] > temp && j > 0){
            this.array[j] = this.array[j - 1]//大的数据右移
            j--
          }
          //4.while循环结束后,index = j左边的数据变为局部有序且array[j]最大。此时将array[j]重置为排序前的数据array[i],方便下一次for循环
          this.array[j] = temp
        }
      }

La eficiencia del tipo de inserción:

  • Número de comparaciones: En la primera pasada, el número máximo de veces requeridas es 1; en la segunda vez, el número máximo es 2; y así sucesivamente, la última pasada es hasta N-1; por lo tanto, el número total de comparaciones para la ordenación por inserción es N * (N - 1 ) / 2; Sin embargo, de hecho, solo la mitad de todos los elementos de datos deben compararse en promedio antes de que se encuentre el punto de inserción cada vez, por lo que el número de comparaciones es: N * ( N - 1) / 4 ;

  • Número de intercambios: cuando el primer dato se especifica como X, debe intercambiarse 0 veces, y cuando el segundo dato se especifica como X, debe intercambiarse como máximo una vez, y así sucesivamente. como X, debe intercambiarse como máximo N - 1 veces, por lo que el total Necesidad de intercambiar N * (N - 1) / 2 veces, y el número de sorteos es N * (N - 1) / 2 ;

  • Aunque la eficiencia de la clasificación por inserción es O (N ^ 2) en notación O grande , el número total de operaciones de clasificación por inserción es menor. Por lo tanto, en la clasificación simple, la clasificación por inserción es la más eficiente ;

4. Clasificación de colinas

Hill sort es una versión mejorada y eficiente de la ordenación por inserción , que es más eficiente que la ordenación por inserción .

Antecedentes históricos del género Hill:

  • Hill sorting lleva el nombre de su diseñador Hill (Donald Shell), y el algoritmo se publicó en 1959 ;
  • Por primera vez, el algoritmo Hill superó la complejidad temporal del algoritmo que la industria informática siempre ha creído que es O (N ^ 2) Para conmemorar el significado histórico del algoritmo, el algoritmo lleva el nombre de Shell ;

Problema de clasificación por inserción:

  • Suponga que un elemento de datos pequeño está ubicado muy cerca del extremo derecho , que debería ser la posición de un elemento de datos más grande ;
  • Para mover este pequeño elemento de datos a la posición correcta a la izquierda, todos los elementos de datos intermedios deben desplazarse un bit a la derecha , lo cual es muy ineficiente;
  • Si hubiera alguna forma de mover los elementos de datos más pequeños hacia la izquierda sin mover todos los elementos de datos intermedios uno por uno, entonces la velocidad del algoritmo mejoraría considerablemente.

La idea de implementación de la clasificación Hill:

  • La clasificación por colinas realiza principalmente una clasificación rápida mediante la agrupación de datos;
  • Divida los datos en grupos de brecha de acuerdo con el incremento establecido (brecha) ( el número de grupos es igual a la brecha ), y luego realice una clasificación parcial en cada grupo;

Si hay 10 datos en una matriz, el primer dato es negro y el incremento es 5. Luego, el segundo índice de datos negros = 5 y el tercer índice de datos negros = 10 (no existe). Por lo tanto, solo hay 2 datos negros en cada grupo, y 10/2 = 5 se puede dividir en 5 grupos en total, es decir, la cantidad de grupos es igual a la brecha incremental .

  • Después de clasificar, reduzca el incremento, continúe agrupando y vuelva a realizar la clasificación local hasta que la brecha de incremento sea igual a 1. La clasificación de la matriz se puede completar con solo ajustes menores;

El proceso específico es el siguiente:

  • Antes de ordenar, la matriz original que almacena 10 datos es:

  • Establezca el intervalo de incremento inicial = longitud / 2 = 5, es decir, la matriz se divide en 5 grupos, como se muestra en la figura: [8, 3], [9, 5], [1, 4], [7, 6] , [2, 0]:

  • Luego ordene los datos localmente en cada grupo, el orden de los cinco grupos se muestra en la figura, que se convierte en: [3, 8], [5, 9], [1, 4], [6, 7], [0 , 2]:

  • Luego reduzca la brecha de incremento = 5 / 2 = 2, es decir, la matriz se divide en 2 grupos, como se muestra en la figura: [3, 1, 0, 9, 7], [5, 6, 8, 4, 2] :

  • Luego, los datos se ordenan localmente en cada grupo y el orden de los dos grupos se muestra en la figura, que se convierte en: [0, 1, 3, 7, 9], [2, 4, 5, 6, 8]:

  • Luego reduzca la brecha de incremento = 2 / 1 = 1, es decir, la matriz se divide en 1 grupo, como se muestra en la figura: [0, 2, 1, 4, 3, 5, 7, 6, 9, 8] :

  • Finalmente, solo necesita realizar una ordenación por inserción en el conjunto de datos para completar la ordenación de toda la matriz:

Opciones incrementales:

  • El espaciado inicial sugerido por Hill en el manuscrito original es N / 2. Por ejemplo, para una matriz de N = 100, la secuencia de incremento es: 50, 25, 12, 6, 3, 1. Se puede encontrar que es redondeado hacia abajo cuando no es divisible.
  • Secuencia incremental Hibbard: El algoritmo de secuencia incremental es: 2^k - 1, es decir, 1, 3, 5, 7... etc., la peor complejidad en este caso es O(N3/2)** , la media la complejidad es **O(N5/4) pero no probada;
  • Secuencia de incremento de Sedgewcik:

La siguiente implementación de código adopta el incremento sugerido en el manuscrito de clasificación de Hill, que es N/2  .

Código:

      //希尔排序
      ArrayList.prototype.shellSort = function(){
        //1.获取数组的长度
        let length = this.array.length

        //2.初始化增量
        let gap = Math.floor(length / 2)

        //3.第一层循环:while循环(使gap不断减小)
        while(gap >= 1 ){
          //4.第二层循环:以gap为增量,进行分组,对分组进行插入排序
          //重点为:将index = gap作为选中的第一个数据
          for(let i = gap; i < length; i++){
            let temp = this.array[i]
            let j = i
            //5.第三层循环:寻找正确的插入位置
            while(this.array[j - gap] > temp && j > gap - 1){
              this.array[j] = this.array[j - gap]
              j -= gap
            }
          //6.将j位置的元素设置为temp
          this.array[j] = temp
          }

          gap = Math.floor(gap / 2)
        }
      }

Aquí hay una explicación del bucle de tres capas en el código anterior:

  • La primera capa de bucle: mientras que el bucle, la brecha de control se reduce a 1;
  • La segunda capa del ciclo: Saque los datos del grupo de brecha divididos de acuerdo con la brecha de incremento g: tome los datos con índice = brecha como los primeros datos seleccionados, como se muestra en la figura a continuación, si la brecha = 5, entonces los datos con índice = brecha es 3, Los datos con índice = brecha - 1 es 8, y los dos datos son un grupo. Luego, la brecha se incrementa continuamente en 1 y se mueve hacia la derecha hasta que la brecha <longitud.En este momento, la matriz se divide en 5 grupos.

  • La tercera capa de bucle: inserta y ordena cada conjunto de datos;

La eficiencia del ordenamiento de Hill:

  • La eficiencia de la clasificación de Hill está directamente relacionada con el incremento, incluso si se usa la eficiencia incremental en el manuscrito original, es mayor que la de la clasificación simple.

5. Clasificación rápida

Introducción a la ordenación rápida:

  • Se puede decir que la clasificación rápida es el algoritmo de clasificación más rápido entre todos los algoritmos de clasificación en la actualidad . Por supuesto, ningún algoritmo es óptimo en todas las situaciones. Sin embargo, quicksort es una mejor opción en la mayoría de los casos.

  • Quick sort es en realidad una versión mejorada de bubble sort ;

La idea central de la ordenación rápida es dividir y conquistar , primero seleccione un dato (como 65), coloque los datos más pequeños en el lado izquierdo y coloque los datos más grandes en el lado derecho. . Estos datos se llaman pivote.

A diferencia del tipo de burbuja:

  • El 65 que elegimos podemos ponerlo en la posición más correcta en un momento, y no hay necesidad de hacer ningún movimiento después;
  • Incluso si la ordenación de burbujas ha encontrado el valor máximo, debe continuar moviendo el valor máximo hasta que se mueva hacia el extremo derecho;

Centro de clasificación rápida:

  • La primera solución: seleccione directamente el primer elemento como centro. Sin embargo, cuando el primer elemento es el valor mínimo, la eficiencia no es alta;
  • La segunda opción: usar números aleatorios. El número aleatorio en sí consume mucho rendimiento y no se recomienda;
  • Excelente solución: tome index como el número del medio después de ordenar los tres datos de head, middle y bit; como se muestra en la figura a continuación, los tres datos extraídos por el valor del subíndice son: 92, 31, 0, que se convierten después de ordenar: 0, 31, 92, toma el número del medio 31 como pivote (cuando (longitud-1)/2 no es divisible, se puede redondear hacia abajo o hacia arriba):

 Implementar selección de concentrador:

//交换两个位置的数据
let swap = function(arr, m, n){
    let temp  = arr[m]
    arr[m] = arr[n]
    arr[n] = temp
}

//快速排序
//1.选择枢纽
let median = function(arr){
  //1.取出中间的位置
  let center = Math.floor(arr.length / 2)
  let right = arr.length - 1 
  let left = 0

  //2.判断大小并进行交换
  if (arr[left] > arr[center]) {
    swap(arr, left, center)
  }
  if (arr[center] > arr[right]){
    swap(arr, center, right)
  }
  if (arr[left] > arr[right]) {
    swap(arr, left, right)
  }
  //3.返回枢纽
  return center
}

Después de que la matriz es operada por la función de pivote de adquisición, las posiciones de datos correspondientes a los tres valores de subíndice seleccionados se convierten en:

 Implementación rápida del código de clasificación:

//2.快速排序
let QuickSort = function(arr){
  if (arr.length == 0) {
    return []
  }
  let center = median(arr)
  let c = arr.splice(center, 1)
  let l = []
  let r = []

  for (let i = 0; i < arr.length; i++) {
      if (arr[i] < c) {
        l.push(arr[i])
      }else{
        r.push(arr[i])
      }        
  }
  return QuickSort(l).concat(c, QuickSort(r))
}

Eficiencia de clasificación rápida:

  • Eficiencia en el peor de los casos de clasificación rápida: el concentrador seleccionado cada vez es el dato más a la izquierda o más a la derecha. En este momento, la eficiencia es equivalente a la clasificación de burbujas y la complejidad del tiempo es O(n2) . Esto se puede evitar dependiendo de las opciones del concentrador;
  • La eficiencia promedio de la clasificación rápida: O(N*logN) , aunque la eficiencia de otros algoritmos también puede llegar a O(N*logN), entre ellos, la clasificación rápida es la mejor .

Supongo que te gusta

Origin blog.csdn.net/m0_65835778/article/details/127069411
Recomendado
Clasificación