Invert Simple List

1
  

EXERCISE: Type a method that invokes a linked list:    Entry: 10, 99, 101, 666
Expected result: 666, 101, 99, 10

I thought I would follow the following logic: - I would get to the last element (666) with pe in the penultimate with q (101), would have p.prox indicate until q (ie, inverting these two, making 666 - new head - indicate for 101). Ai, in a loop, would make him repeat this process only by going to the antelopenultimate (99) and to the last penultimate (10), reversing them also, until reaching the end. But something is going wrong and I can not find what. Can someone help me?

class E4 {

    ListaSimples e4 = new ListaSimples();
    public void inverterLista () {
        e4.insere(10);
        e4.insere(99);
        e4.insere(101);
        e4.insere(666);
        int nElementos = 4;
        int n;
        int k = nElementos;

        for (int i=0; i<nElementos; i++) {
            n = 1;
            No p = e4.cabeca;
            No q = e4.cabeca;

            // nessa parte faz com que ele chegue ate o numero e na
            // proxima vez, ate um numero menor e assim vai
            while (n<k) {
                q = p;
                p = p.prox;
                n++;
            }
            p.prox = q;
            k--;
        }

        for (int m=0; m<nElementos; m++){
            No imprimir = e4.cabeca;
            System.out.print(imprimir.letra+" ");
            imprimir = imprimir.prox;
        }

    }

    public static void main(String[] args) {
        E4 e4 = new E4 ();
        e4.inverterLista();
    }

}
    
asked by anonymous 03.07.2017 / 21:33

2 answers

3

There are several alternatives for doing this inversion. Keep in mind that you can not miss the next element in the list, you should always keep a reference to it.

I like the recursive alternative to this question, but this is a very simple recursion, so I'll show its iterative equivalent.

Recursive version

A simply linked list inversion is the process of doing the following:

ancestral -> atual -> descendente
// magia da inversão 
ancestral <- atual <- descendente

It basically consists of two steps:

  • Makes the current pointing to the ancestor
  • Reverse the descendant
  • The order of steps is irrelevant as long as the value of atual and descendente is always known. So I'm going to create a inverte method that will get a hypothetical ancestral and a atual . Note that the first element of the list has no ancestor, so I can call it from null (or it can be a guardian element, anyway). Since I have no guarantee that the last element of the list points to the null value, I will also pass the position of the current element and the amount of elements in the list.

    Strategy to follow:

  • If I did not reach the end ( posAtual < tamanho ), I do steps 2 and 3
  • I'm invoking the atual.prox element in the argument for atual , atual in argument ancestral and posAtual + 1 in argument to posAtual
  • atual.prox = ancestral
  • Note that if I save the value of atual.prox to the variable descendente , I can reverse steps 2 and 3. In Java, I'll call the recursion like this:

    No novaCabeca = inverte(null, e4.cabeca, 0, nElementos);
    e4.cabeca = novaCabeca; // para manter a cabeça válida 
    
      

    I am particularly in favor of having an element counter within ListaSimples , or having the assurance that the last element points to null or to a guardian element.

    Recursion is implemented like this:

    public static No inverter(No ancestral, No atual, int posAtual, int nElementos) {
        // passo 1: se for além do tamanho da lista, não processa nada
        if (posAtual >= nElementos) {
            return ancestral; // último nó válido
        }
        // passo 2: inverter a lista descendente
        No fimLista = inverter(atual, atual.prox, posAtual + 1, nElementos);
        // passo 3: inverter o nó atual
        atual.prox = ancestral;
    
        return fimLista;
    }
    

    Out of curiosity, this one was a recursion of head, because the recursive step is in the beginning. The memory required to do this recursion is o(nElementos) , execution time is also o(nElementos) .

    Turning into iteration

    I will transform this function first into a tail recursion, to then remove the recursive step and make it completely iterative.

    To transform into tail recursion, simply reverse steps 2 and 3. As stated above, you only need one variable to do this:

    public static No inverter(No ancestral, No atual, int posAtual, int nElementos) {
        // passo 1: se for além do tamanho da lista, não processa nada
        if (posAtual >= nElementos) {
            return ancestral; // último elemento válido
        }
        // reservando o valor do próximo 
        No prox = atual.prox;
        // passo 3: inverter o nó atual
        atual.prox = ancestral;
        // passo 2: inverter a lista descendente
        return inverter(atual, prox, posAtual + 1, nElementos);
    }
    

    Note that posAtual is serving as iteration index, so we can change all this by a for , where the stop condition is the stop condition at the end of the recursion:

    public static void inverterIterativo(ListaSimples l, int nElementos) {
        // simulando primeira chamada recursiva, note que os valores são os mesmos
        No ancestral = null;
        No atual = l.cabeca;
        for (int posAtual = 0; posAtual < nElementos; posAtual++) {
            No prox = atual.prox; // guarda o descendente para inverter no próximo passo
            atual.prox = ancestral; // inverte o atual
    
            // quando da chamada iterativa, atual é chamado com o valor de prox e ancestral com o valor de atual
    
            ancestral = atual;
            atual = prox;
        }
    
        // note que o último elemento válido ficou armazenado na variável ancestral
        l.cabeca = ancestral;
    }
    

    Note that this method now uses additional% constant memory%, in contrast to the additional recursion memory that is o(1) . The time, however, continues in the same asymptotic behavior of o(nElementos) .

    Other alternatives?

    At the moment, I think of another alternative to write to a vector of size o(nElementos) and copy it back to the nodes in the list. Here we will have a reversal of values, not addresses. Since you are doing an exercise, I believe this is going to be the alternative that would give you less points, but continue to reverse (and I think it's a valid curiosity).

    Note that it will be necessary to go through the list twice now, and that additional memory of nElementos is used.

    public static void inverterIterativo(ListaSimples l, int nElementos) {
        int[] valores = new int[nElementos];
        // primeira iteração: povoa o vetor
        for (int i = 0, No atual = l.cabeca; i < nElementos; i++, atual = atual.prox) {
            valores[i] = atual.letra;
        }
        // segunda iteração: transmite os valores do vetor para os nós
        for (int i = nElementos - 1, No atual = l.cabeca; i >= 0; i--, atual = atual.prox) {
            atual.letra = valores[i]; // posição de i varia de traz para frente
        }
    }
    
        
    04.07.2017 / 05:31
    0

    I do not know if I understood the right question, but to reverse the positions, you can do this:

    1) Creates a counter in the class and increments it within the insere method of class ListaSimples :

    public void insere(int numero) {
        // INSTRUÇÕES PARA ADICIONAR NA LISTA
        cont++;
    }
    

    2) Make a loop that decrements the value of the variable cont :

    int j = 0;
    
    for (int i = cont; i > 0; i--) {
        numerosAoContrario[j] = numerosInformados[i];
        j++;
    }
    

    The variable i will receive the number of numbers reported by the insere method and the loop will decrement the value by one by one until it reaches zero (this means that you are accessing the values backwards).

    The variable j will be instantiated outside the loop and incremented inside it, so numerosAoContrario[0] = numerosInformados[4] (as in your example).

        
    04.07.2017 / 05:56