Matriz de classificação LeetCode912 (explicação detalhada da máquina de classificação rápida e sua otimização)

Matriz de classificação LeetCode912 (explicação detalhada da classificação rápida e sua otimização)

Abstrato

Tentei primeiro usar o algoritmo de classificação rápida para resolver esse problema, mas não consegui exceder o limite de tempo. Os dois casos de teste que excedem o limite de tempo são: 1. Todos os elementos do array são iguais 2. O array já está classificado em ordem crescente. Para o primeiro caso de teste, o método do ponteiro de três direções pode ser usado (ou seja, os elementos iguais ao elemento pivô são listados em um bloco separado). Para o segundo caso de teste, você pode usar o método de seleção aleatória do elemento pivô (usando a função Rand() de C++% o comprimento da matriz a ser classificada + o subscrito inicial da matriz a ser classificada). Neste artigo, primeiro apresento o método básico de classificação rápida e, em seguida, apresento duas ideias de otimização. No final, usei duas ideias de otimização ao mesmo tempo e finalmente passei no Leetcode912.

Classificação rápida básica

Assim como a classificação por mesclagem, a classificação rápida também usa a ideia de dividir e conquistar. A seguir está um processo de divisão e conquista em três etapas para classificação rápida de um subarray típico A[p...r].

Decomposição: A matriz A[p…r] é dividida em duas submatrizes (possivelmente vazias) A[p…q-1] e A[q+1…r], de modo que em A[p…q-1] Cada elemento of é menor ou igual a A[q], e A[q] também é menor ou igual a cada elemento em A[q+1…r]. Dentre eles, o cálculo do subscrito q também faz parte do processo de divisão.

**Solução:** Classifique as submatrizes A[p...q-1] e A[q+1...r] chamando recursivamente a classificação rápida.

Mesclar: como os subarrays são classificados no local, não há necessidade de uma operação de mesclagem: o array A[p...r] já está classificado.

Pseudocódigo para classificação rápida

Classificação rápida (A, p, r)

se p<r

​ q = PARTIÇÃO(A, p,r)

QuickSort(A, p, q-1)

QuickSort(A, q+1, r)

Para classificar todos os elementos de um array A, a chamada inicial é QUICKSORT(A, 1, A.length)

Divisão de matriz

A parte principal do algoritmo é o processo PARTITION, que implementa o rearranjo local do subarranjo A[p...r].

PARTIÇÃO(A, p,r)

x = UMA[r]

eu = p-1

para j = p para r-1

​ se(A[j]<=x)

eu++;

troca(A[j], A[i])

troca(A[i+1], A[r])

retornar i+1;

O ponto chave para dominar este algoritmo é saber quais quantidades mudam e quais quantidades não mudam. O que muda são os dois ponteiros i e j, e o que permanece inalterado é p e r. Só precisamos saber como alterar i e j durante o processo de digitalização.

O que precisamos garantir é:

  • A[p…i]是<=x
  • A[i+1…j-1]是>=x.
  • A[j…r-1] é desconhecido
  • A[r] é igual a x.

Se A[j]<=x, você precisa colocar o elemento na parte menor ou igual a x, primeiro i++, depois trocar A[j] e A[i], depois j++;

Se A[j]>x, então apenas j++ será necessário.

Quando digitalizamos para A[r], precisamos colocar o elemento divisor na posição que definimos para o elemento divisor, ou seja, a posição de i+1. Então troque A[r] e A[i+1] e retorne i+1;

Resumo e otimização de ideias de classificação rápida

  • Ideia básica: a classificação rápida organiza um elemento de cada vez, depois classifica recursivamente a parte esquerda e a parte direita e prossegue em sequência até que a matriz seja classificada.

  • Conforme mostrado na imagem: Esta imagem reflete bem que a essência da classificação rápida é na verdade uma árvore recursiva.

    [Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-hOIWXXyb-1687253345354)(https://pic.leetcode.cn/1678475287-xpnAxX -%E5%9F%BA%E7%A1%80%E5%BF%AB%E6%8E%92%20-%20%E5%89%AF%E6%9C%AC.jpg)]

Problemas com esta divisão

1. Se todos os elementos da matriz forem iguais e o número de elementos for particularmente grande, é provável que ocorra um tempo limite. Por exemplo, [2,2,2,2,2], após a chamada, haverá [2,2,2,2], depois [2,2,2], depois [2,2] e, finalmente [2].Então a complexidade do tempo é O ( ∑ i = 1 n ) = O ( n 2 ) O(\sum_{i=1}^n) = O(n^2)O ( eu = 1não)=Ó ( n2 )Neste momento, a árvore basicamente classificada é uma árvore com apenas um lado.

2. Se os elementos da matriz foram basicamente classificados, por exemplo, uma matriz de 1 a 50.000 foi classificada em ordem crescente. Para esta matriz, a classificação rápida básica é dividida em:

[A transferência da imagem do link externo falhou. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-1DtPzVDq-1687253345356) (2023-6-19 Quick sorting details/1.jpg )]

Ideias de otimização

Para esta estrutura de árvore, nossa otimização pode partir de dois aspectos: um é equilibrar ao máximo as duas extremidades da árvore e o outro é reduzir ao máximo a altura da árvore.

Para matrizes ordenadas progressivamente

  • Ao obter aleatoriamente o pivô, as duas pontas da árvore ficarão mais equilibradas.

Para matrizes com muitos elementos idênticos

Fila rápida de três vias

  • A classificação rápida de três vias (método de três ponteiros) coloca todos os elementos iguais ao elemento pivô no meio do intervalo dividido. Ou seja, o que determinamos recursivamente a cada vez é a posição desse elemento e de seus elementos iguais. Quando um grande número de elementos é igual. Abaixo, o intervalo de recursão é bastante reduzido.
  • Particionamento de matriz:
    • A[p…i] < x, A[i+1…j-1]==x, A[j…k-1] 未知, A[k…r-1]>x. UMA[r] == x.

Código

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        srand((unsigned)time(NULL));
        int size_nums = nums.size();
        quicksort(nums, 0, size_nums-1);
        return nums;
    }

    vector<int> partition(vector<int>& nums, int p, int r){
        int rand_index = rand() % (r-p+1)+p;
        int x = nums[rand_index];
        swap(nums[rand_index], nums[r]);
        int i = p -1;
        int j = p;
        int k = r;
        //循环不变量
        // nums[p..i] < x
        // nums[i+1..j-1] equals x
        // nums[j..k-1] unknown
        // nums[k..r-1] >x
        // nums[r] equals x
        // 循环不变量在第一轮迭代之前是成立的。
        while(j<=k-1){
            if(nums[j]<x){
                i++;
                swap(nums[j], nums[i]);
                j++;
            }
            else if(nums[j] == x){
                j++;
            }
            else{
                k--;
                swap(nums[j], nums[k]);
            }
        }

        swap(nums[k], nums[r]);
        vector<int> results;
        results.push_back(i);
        results.push_back(k);
        return results;
    }

    void quicksort(vector<int>& nums, int p, int r){
        if(p>=r){
            return;
        }

        if(p<r){
            //随机选取法
            vector<int> partitions = partition(nums, p, r);
            quicksort(nums, p, partitions[0]);
            quicksort(nums, partitions[1], r);
        }
    }
};

Outra implementação de código de classificação rápida básica

A classificação rápida tem dois pontos principais, nomeadamente "divisão sentinela" e "recursão".

Operação de divisão sentinela: Tomando um determinado elemento da matriz (geralmente o primeiro elemento é selecionado) como o número base, mova todos os elementos menores que o número base para a esquerda e mova os elementos maiores que o número base para a direita.

Através de uma rodada de divisão sentinela, o problema de classificação de matrizes pode ser dividido no problema de classificação de duas matrizes mais curtas (chamadas de submatrizes esquerda (direita) neste artigo).

Recursão: execute recursivamente a divisão sentinela no subarray esquerdo e no subarray direito até que o comprimento do subarray seja 1. A recursão é encerrada e todo o array pode ser classificado.

Código de amostra

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        quickSort(strs, 0, nums.size() - 1);
        return nums;
    }
private:
    void quickSort(vector<string>& strs, int l, int r) {
        if(l >= r) return;
        int i = l, j = r;
        while(i < j) {
            while(nums[j] >= nums[l] && i < j) j--;
            while(nums[i] <= nums[l] && i < j) i++;
            swap(nums[i], nums[j]);
        }
        swap(nums[i], nums[l]);
        quickSort(nums, l, i - 1);
        quickSort(nums, i + 1, r);
    }
};

referências

[1] https://leetcode.cn/problems/sort-an-array/solution/kuai-su-pai-xu-you-hua-zhen-dui-duo-zhon-ryq4/

[2] A segunda implementação de código de classificação rápida https://leetcode.cn/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof/solution/mian-shi-ti-45 - ba-shu-zu-pai-cheng-zui-xiao-de-s-4/

Acho que você gosta

Origin blog.csdn.net/ChenglinBen/article/details/131311964
Recomendado
Clasificación