Estrutura de dados Javascript e notas de estudo de algoritmo (abaixo - algoritmo de classificação)

1. Notação O grande

Grande notação O:

  • O uso de medidas aproximadas em computadores para descrever a eficiência dos algoritmos de computador é conhecido como notação "big O"
  • Quando o número de itens de dados muda, a eficiência do algoritmo também muda. Portanto, não faz sentido dizer que o Algoritmo A é duas vezes mais rápido que o Algoritmo B.
  • Portanto, geralmente usamos como a velocidade do algoritmo mudará com a alteração da quantidade de dados para expressar a eficiência do algoritmo, e a grande notação O é uma das maneiras.

Notação O grande comum

 Complexidade de tempo de diferentes formas grandes de O:

 Pode-se ver que a eficiência de grande para pequeno é: O(1) > O(logn) > O(n) > O(nlog(n)) > O(n²) > O(2n)

Três regras para derivar a notação Big O:

  • Regra 1 : Substitua todas as constantes aditivas em tempo de execução pela constante 1. Por exemplo, 7 + 8 = 15, 1 é usado para representar o resultado da operação 15 e a grande notação O é expressa como O(1);
  • Regra 2 : Apenas o termo de maior ordem é mantido na operação. Tal como N^3 + 3n +1, a grande notação O é expressa como: O(N3);
  • Regra 3 : Se a constante do termo de ordem mais alta não for 1, ela pode ser omitida. Tal como 4N2, a grande notação O é expressa como: O(N2);

2. Algoritmo de classificação

Aqui estão várias classificações simples e avançadas:

  • Classificação simples: classificação por bolha, classificação por seleção, classificação por inserção;
  • Classificação avançada: classificação de colina, classificação rápida;

Crie uma classe de lista ArrayList aqui e adicione algumas propriedades e métodos para armazenar esses métodos de classificação:

    //创建列表类
    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. Classificação de bolhas

Ideia de classificação de bolhas:

  • Para cada elemento não classificado, compare a relação de tamanho dos dois elementos adjacentes do começo ao fim ;
  • Se o elemento à esquerda for maior , as posições serão trocadas . Por exemplo, 1 é menor que 2, não troque de posição;
  • Mova um bit para a direita , continue comparando 2 e 3 e, finalmente, compare os dois dados de comprimento - 1 e comprimento - 2;
  • Ao chegar à extrema direita , a pessoa mais alta deve ser colocada à extrema direita ;
  • De acordo com essa ideia, ao reiniciar da extremidade esquerda, basta ir até a penúltima posição ;

Ideias de implementação:

Duas voltas:

  • O loop externo controla o número de vezes de borbulhamento:

    • A primeira vez: j = comprimento - 1, em comparação com a primeira última posição;
    • A segunda vez: j = comprimento - 2, em comparação com a penúltima posição;
  • O loop interno controla o número de comparações por passagem:

    • A primeira comparação: i = 0, compara dois dados nas posições 0 e 1;
    • A última comparação: i = comprimento - 2, comparar comprimento - 2 e comprimento - 1 dois dados;

O processo detalhado é mostrado na figura abaixo:

 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
          }
        }
        }
      }

Eficiência do tipo de bolha:

  • Para os 7 itens de dados mencionados acima, o número de comparações é: 6 + 5 + 4 + 3 + 2 + 1;
  • Para N itens de dados, o número de comparações é: (N - 1) + (N - 2) + (N - 3) + ... + 1 = N * (N - 1) / 2; se duas comparações forem trocadas uma vez , então o número de trocas é: N * (N - 1) / 4;
  • Use a notação O grande para representar o número de comparações e o número de trocas respectivamente: O( N * (N - 1) / 2) e O( N * (N - 1) / 4), de acordo com as três regras de a grande notação O Abreviação de: O(N^2) ;

2. Classificação de seleção

A classificação por seleção melhora a classificação por bolhas:

  • Reduzir o número de trocas de O(N^2) para O(N) ;
  • Mas o número de comparações ainda é O(N^2) ;

A ideia de classificação por seleção:

  • Selecione a posição do primeiro índice, como 1, e compare-o com os seguintes elementos sucessivamente ;
  • Se o elemento seguinte for menor que o elemento no índice 1, troque a posição para o índice 1;
  • Após uma rodada de comparação, pode-se determinar que o elemento no índice 1 especificado no início é o menor ;
  • Em seguida, use o mesmo método para comparar os elementos restantes, um por um, exceto o índice 1 ;
  • Pode-se observar que na ordenação por seleção, o valor mínimo será selecionado na primeira rodada , e o segundo menor valor será selecionado na segunda rodada até que a ordenação seja concluída.

Ideias de implementação:

Duas voltas:

  • O loop externo controla o índice especificado:

    • A primeira vez: j = 0, especificando o primeiro elemento;
    • Última vez: j = comprimento - 1, especifique o último elemento;
  • O loop interno é responsável por comparar o elemento no índice especificado (i) com o 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)
        }
      }

Eficiência da ordenação por seleção:

  • O número de comparações para classificação por seleção é: N * (N - 1) / 2, expresso em notação O grande como: O(N^2) ;
  • O número de trocas para ordenação por seleção é: (N - 1) / 2, expresso em notação O grande como: O(N) ;
  • Portanto, a eficiência da ordenação por seleção é maior do que a ordenação por bolhas;

3. Classificação por inserção

A classificação por inserção é a classificação mais eficiente na classificação simples .

A ideia de classificação por inserção:

  • O núcleo da ideia de ordenação por inserção é a ordem local . Conforme mostrado na figura, a pessoa à esquerda de X é chamada de ordem local ;
  • Primeiro, especifique um dado X (começando do primeiro dado) e altere o lado esquerdo do dado X para um estado local ordenado;
  • Em seguida, mova X um bit para a direita e, após atingir a ordem local novamente, continue movendo um bit para a direita e repita a operação anterior até que X se mova para o ú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
        }
      }

A eficiência da ordenação por inserção:

  • Número de comparações: Na primeira passagem, o número máximo de vezes necessário é 1; na segunda vez, o número máximo é 2; e assim por diante, a última passagem é até N-1; portanto, o número total de comparações para classificação por inserção é N * (N - 1 ) / 2; No entanto, na verdade, apenas metade de todos os itens de dados precisam ser comparados em média antes que o ponto de inserção seja encontrado a cada vez, então o número de comparações é: N * ( N - 1) / 4 ;

  • Número de trocas: Quando o primeiro dado é especificado como X, ele precisa ser trocado 0 vezes, e quando o segundo dado é especificado como X, ele precisa ser trocado no máximo uma vez, e assim por diante. Quando o N-ésimo dado é especificado como X, precisa ser trocado no máximo N - 1 vezes, então o total Necessidade de troca N*(N-1)/2 vezes, e o número de empates é N*(N-1)/ 2 ;

  • Embora a eficiência da classificação por inserção seja O(N^2) na notação O grande , o número total de operações de classificação por inserção é menor. Portanto, na classificação simples, a classificação por inserção é a mais eficiente ;

4. Tipo de colina

Hill sort é uma versão melhorada e eficiente da classificação por inserção , que é mais eficiente do que a classificação por inserção .

Antecedentes históricos da família Hill:

  • A classificação de Hill recebeu o nome de seu criador Hill (Donald Shell), e o algoritmo foi publicado em 1959 ;
  • Pela primeira vez , o algoritmo de Hill ultrapassou a complexidade de tempo do algoritmo que a indústria de computadores sempre acreditou ser O(N^2) .

Problema de ordenação por inserção:

  • Suponha que um pequeno item de dados esteja localizado muito próximo à extremidade direita , que deveria ser a posição de um item de dados maior ;
  • Para mover este pequeno item de dados para a posição correta à esquerda, todos os itens de dados intermediários devem ser deslocados um bit para a direita , o que é muito ineficiente;
  • Se houvesse alguma maneira de mover os itens de dados menores para a esquerda sem mover todos os itens de dados do meio, um por um, a velocidade do algoritmo seria muito melhorada.

A ideia de implementação da classificação de Hill:

  • A classificação de colina realiza principalmente a classificação rápida agrupando dados ;
  • Divida os dados em grupos de lacunas de acordo com o incremento definido (gap) ( o número de grupos é igual a gap ) e, em seguida, execute a classificação parcial em cada grupo;

Se houver 10 dados em uma matriz, o primeiro dado é preto e o incremento é 5. Em seguida, o segundo índice de dados pretos = 5 e o terceiro índice de dados pretos = 10 (não existe). Portanto, existem apenas 2 dados pretos em cada grupo e 10/2 = 5 podem ser divididos em 5 grupos no total, ou seja, o número de grupos é igual ao intervalo incremental .

  • Após a classificação, reduza o incremento, continue agrupando e execute a classificação local novamente até que o intervalo de incremento seja igual a 1. A classificação da matriz pode então ser concluída com apenas pequenos ajustes;

O processo específico é o seguinte:

  • Antes da classificação, a matriz original que armazena 10 dados é:

  • Defina o intervalo de incremento inicial = comprimento / 2 = 5, ou seja, a matriz é dividida em 5 grupos, conforme mostrado na figura: [8, 3], [9, 5], [1, 4], [7, 6], [2, 0]:

  • Em seguida, classifique os dados localmente em cada grupo, a ordem dos cinco grupos é mostrada na figura, que se torna: [3, 8], [5, 9], [1, 4], [6, 7], [0 , 2]:

  • Em seguida, reduza o intervalo de incremento = 5 / 2 = 2, ou seja, a matriz é dividida em 2 grupos, conforme mostrado na figura: [3, 1, 0, 9, 7], [5, 6, 8, 4, 2] :

  • Os dados são então classificados localmente em cada grupo, e a ordem dos dois grupos é mostrada na figura, que se torna: [0, 1, 3, 7, 9], [2, 4, 5, 6, 8]:

  • Em seguida, reduza o intervalo de incremento = 2 / 1 = 1, ou seja, a matriz é dividida em 1 grupo, conforme mostrado na figura: [0, 2, 1, 4, 3, 5, 7, 6, 9, 8] :

  • Por fim, você só precisa executar a classificação por inserção no conjunto de dados para concluir a classificação de todo o array:

Opções incrementais:

  • O espaçamento inicial sugerido por Hill no manuscrito original é N / 2. Por exemplo, para uma matriz de N = 100, a sequência de incremento é: 50, 25, 12, 6, 3, 1. Pode-se descobrir que é arredondado para baixo quando não é divisível.
  • Sequência incremental de Hibbard: O algoritmo da sequência incremental é: 2^k - 1, ou seja, 1, 3, 5, 7... etc.; a pior complexidade neste caso é O(N3/2)** , a média a complexidade é **O(N5/4) mas não comprovada;
  • Sequência de incremento de Sedgewcik:

A implementação de código a seguir adota o incremento sugerido no manuscrito de classificação de Hill, que é 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)
        }
      }

Aqui está uma explicação do loop de três camadas no código acima:

  • A primeira camada do loop: while loop, o gap de controle é decrementado para 1;
  • A segunda camada do loop: Retire os dados do grupo gap divididos de acordo com o incremento g gap: pegue os dados com índice = gap como os primeiros dados selecionados, conforme mostrado na figura abaixo, se gap=5, então os dados com índice = intervalo é 3, os dados com índice = intervalo - 1 são 8 e os dois dados são um grupo. Em seguida, o intervalo é continuamente aumentado em 1 e movido para a direita até que o intervalo <comprimento. Neste momento, a matriz é dividida em 5 grupos.

  • A terceira camada do loop: insira e classifique cada conjunto de dados;

A eficiência da ordenação de Hill:

  • A eficiência da ordenação de Hill está diretamente relacionada ao incremento, mesmo que seja utilizada a eficiência incremental do manuscrito original, ela é superior à da ordenação simples.

5. Classificação rápida

Introdução à ordenação rápida:

  • A ordenação rápida pode ser considerada o algoritmo de ordenação mais rápido entre todos os algoritmos de ordenação atualmente . Claro, nenhum algoritmo é ótimo em todas as situações. No entanto, o quicksort é uma escolha melhor na maioria dos casos.

  • A classificação rápida é, na verdade , uma versão atualizada da classificação por bolha ;

A ideia central da classificação rápida é dividir e conquistar , primeiro selecione um dado (como 65), coloque os dados menores que ele no lado esquerdo dele e coloque os dados maiores que ele no lado direito dele . Esses dados são chamados de pivô

Ao contrário do tipo de bolha:

  • O 65 que escolhemos pode colocá-lo na posição mais correta de uma só vez, não sendo necessário fazer nenhum movimento depois;
  • Mesmo que o tipo de bolha tenha encontrado o valor máximo, ele precisa continuar a mover o valor máximo até que seja movido para a extrema direita;

Hub de classificação rápida:

  • A primeira solução: selecione diretamente o primeiro elemento como hub. Porém, quando o primeiro elemento é o valor mínimo, a eficiência não é alta;
  • A segunda opção: use números aleatórios. O próprio número aleatório consome muito desempenho e não é recomendado;
  • Excelente solução: tome o índice como o número do meio após classificar os três dados de cabeça, meio e bit; conforme mostrado na figura abaixo, os três dados retirados pelo valor subscrito são: 92, 31, 0, que se tornam após a classificação: 0, 31, 92, tome o número do meio 31 como pivô (quando (comprimento-1)/2 não é divisível, pode ser arredondado para baixo ou para cima):

 Implementar seleção de hub:

//交换两个位置的数据
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
}

Depois que a matriz é operada pela função pivô de aquisição, as posições de dados correspondentes aos três valores subscritos selecionados tornam-se:

 Implementação rápida do código de classificação:

//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))
}

Eficiência de classificação rápida:

  • Eficiência no pior caso de classificação rápida: o hub selecionado a cada vez é o dado mais à esquerda ou mais à direita. Neste momento, a eficiência é equivalente à classificação de bolhas e a complexidade de tempo é O(n2 ) . Isso pode ser evitado dependendo das opções de hub;
  • A eficiência média da ordenação rápida: O(N*logN) , embora a eficiência de outros algoritmos também possa chegar a O(N*logN), dentre eles, a ordenação rápida é a melhor .

Acho que você gosta

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