Ordenação por distribuição
Autor: Heitor | Publicado: 2009-11-04T23:54:00-02:00

Veremos agora um algoritmo de ordenação que se propõe a ordenar um conjunto de números em tempo linear. Sabemos que este problema é O(n.log(n)), então atacamos um problema um pouco menos genérico, do qual temos algumas informações a priori: podemos supor que a quantidade de dígitos dos números a serem ordenados é finito (de acordo com uma base) e conhecido.Eis o código:/*** MAC 2301 - Laboratório de Programação** Exercício da aula 4** Ordenação por distribuição (aka radix sort)***/#include #include #define BASEMAX 10#define UNDERFLOW -1#define OVERFLOW -1typedef struct cel{ int chave; struct cel *prox;} celula;/* complexidade desta função: O(tamanho da fila) */void imprimeFila (celula *inicio){ celula *varre = inicio; printf("\n    ordenando -> "); while (varre != NULL) {   printf (" %d ", varre->chave);   varre = varre->prox; } printf("\n\n");}/* complexidade desta função: O(1) = constante */int insereNaFila (int x, celula **inicio, celula **fim){ celula *nova; nova = malloc (sizeof (celula)); if(nova == NULL)   return OVERFLOW; nova->chave = x; nova->prox = NULL; if((*fim) == NULL)   *inicio = nova; else   (*fim)->prox = nova; *fim = nova; return 0;}/* O(1) */int proximoDaFila (int *valorDevolvido, celula **inicio, celula **fim){ celula *morta; if(*inicio == NULL)   return UNDERFLOW; morta = *inicio; *valorDevolvido = (*inicio)->chave; if(morta->prox == NULL)   *fim = NULL; *inicio = morta->prox; free (morta); return 0;}/* O(tamanho da fila) */void removeFila(celula* fila){ celula* morta; while(fila != NULL) {   morta = fila;   fila = fila->prox;   free(morta); }}/* funções auxiliares *//* O(1) */int max(int um, int outro){ if(um > outro)   return um; return outro;}/* O(número de dígitos de umNumero) */int numDigitos(int umNumero){ int numDig = 1; while(umNumero > 0) {   umNumero /= 10;   numDig++; } return numDig;}/* O(i) */int iesimoDigitoMenosSignif(int numero, int i){ while(i > 1) {   numero = numero / 10;   i--; } return numero % 10;}int main(){ celula* inicioDaFilaASerOrdenada; celula* fimDaFilaASerOrdenada; celula* iniciosDasFilas[BASEMAX]; celula* finaisDasFilas[BASEMAX]; int quantDeNumeros, base, numDig; int i, j; /* contadores */ int k, temp; /* variáveis auxiliares */ inicioDaFilaASerOrdenada = NULL; fimDaFilaASerOrdenada = NULL; printf("   Base numérica? \n     -> "); scanf("%d", &amp;base); for(i = 0; i < base; i++) /* laço 1, executado 'base' vezes */ {   iniciosDasFilas[i] = NULL; /* O(1) */   finaisDasFilas[i] = NULL; /* O(1) */ }    printf("   Quantidade de números? \n     -> "); scanf("%d", &amp;quantDeNumeros); printf("   Digite os números a serem ordenados:\n"); /* Agora leremos os números a serem ordenados e, a cada número,  * inserimos esse número na fila principal e contamos seu número  * de dígitos para guardar o maior deles */ numDig = 0; for(i = 0; i < quantDeNumeros; i++) /* laço 2, executado  'quantDeNumeros' vezes */ {   printf("   --> ");                                   /* O(1) */   scanf("%d", &amp;temp);                                  /* O(1) */   insereNaFila(temp, &amp;inicioDaFilaASerOrdenada,        /* O(1) */                &amp;fimDaFilaASerOrdenada);   numDig = max(numDig,                numDigitos(temp));      /* O(número de dígitos de temp) */ } /* o algoritmo consite basicamente em numDig iterações */ for(i = 1; i <= numDig; i++)  /* laço 3, executado 'numDig' vezes */ {   /* primeiro varremos a fila principal e distribuímos    * seus elementos pelas outras filas, de acordo com    * o i-ésimo dígito menos significativo */   for(j = 0; j < quantDeNumeros; j++)  /* laço 3.1, executado    'quantDeNumeros' vezes */   {     proximoDaFila(&amp;temp, &amp;inicioDaFilaASerOrdenada,                   &amp;fimDaFilaASerOrdenada);         /* O(1) */     k = iesimoDigitoMenosSignif(temp, i);         /* O(i) */     insereNaFila(temp, &amp;(iniciosDasFilas[k]),     /* O(1) */                  &amp;(finaisDasFilas[k]));   }   /* agora concatenamos as filas na fila principal */   for(j = 0; j < base; j++) /* laço 3.2, executado 'base' vezes */   {     /* enquanto tem elementos na fila j */     while(proximoDaFila(&amp;temp, &amp;(iniciosDasFilas[j]),                         &amp;(finaisDasFilas[j])) != UNDERFLOW)     { /* laço 3.2.1, executado 'quantDeNumeros' vezes */       insereNaFila(temp, &amp;inicioDaFilaASerOrdenada,                    &amp;fimDaFilaASerOrdenada);  /* O(1) */     }   } } imprimeFila(inicioDaFilaASerOrdenada);   /* O(quantNumeros) */ removeFila(inicioDaFilaASerOrdenada);    /* O(quantNumeros) */ system("pause"); return 0;}/* Análise da complexidade:** Basta somar as complexidades de cada linha. A complexidade de um laço é* dada pelo somatório (variando o contador do laço), da complexidade do seu* bloco de comandos, geralmente em função do contador. Mas se a* complexidade do bloco interno não depender do contador, basta multiplicar* essa complexidade pelo número de iterações do laço. Assim temos:** complexidade do laço 1 = 2 * 'base' = O(base)** complexidade do laço 2 = (3+num de dígitos de temp) * 'quatDeNumeros' =*         [no pior caso] = 3+numDig * 'quantDeNumeros' =*                        = O(numDig*quantDeNumeros)*                       *                 laço 3 = soma (para i = 0 ... 'numDig') de:*                           complexidade do laço 3.1 + complexidade do laço 3.2*                        = soma (para i = 0 ... 'numDig' de:*                                  2+i + 1*'base'*                        = O(2)*'numDig' +*                          (soma (para i = 0 ... 'numDig' de: i) +*                          O(base)*'numDig'*                        = O(numDig) + O(numDig) + O(base*numDig)*                        = O(base*numDig)*                       * complexidade do programa inteiro (função main):*   uma porção de operações de tempo constante (declaração e inicialização*   de variáveis) + soma das complexidades dos laços + complexidade das duas*   últimas linhas =**   = O(1) + O(base) + O(numDig*quantDeNumeros) +*                      O(base*numDig) + 2*O(quantDeNumeros) =*                                        *   = O(numDig*(quantDeNumeros + base))**   Como utilizamos o tipo int, podemos considerar que a base será no*   máximo 10 (pois uma base maior que essa exigiria o uso de caracteres) e*   que o número de dígitos não ultrapassará 64 na maioria dos casos.*   Com essas considerações, o número de números tem potencial para ser bem*   maior que o tamanho da base e que o número de dígitos.*   Assim, a complexidade fica O(64*quantDeNumeros + 640) = O(quantDeNumeros).*   Logo, este algoritmo é linear no número de números a serem ordenados***/