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