There are several alternatives to this inversion. Keep in mind that you cannot lose the next element of 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’m going to show you 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
Basically consists of two steps:
- Makes the current point to the ancestor
- Do the downward flip
The order of the steps is irrelevant as long as the value of the atual and of descendente. So I’m going to create a method inverte who will receive a hypothetical ancestral and a atual. Note that the first element of the list has no ancestor, so I can call it null (or it can be a guardian element, whatever). As I have no guarantee that the last element on the list points to the null value, I will also pass the position of the current element and the amount of elements on the list.
Strategy to be followed:
- If I’m not at the end (
posAtual < tamanho), I do steps 2 and 3
- I call reverse with the element
atual.prox in the argument to atual, atual in the argument ancestral and posAtual + 1 in the argument to posAtual
atual.prox = ancestral
Note that if I save the value of atual.prox in the variable descendente, i can reverse steps 2 and 3. In Java, I’ll call recursion this way:
No novaCabeca = inverte(null, e4.cabeca, 0, nElementos);
e4.cabeca = novaCabeca; // para manter a cabeça válida
I am particularly in favour of having an element counter within ListaSimples, or to have 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 head recursion, because the recursive step is in the beginning. The memory required to make this recursion is o(nElementos), running time is also o(nElementos).
Turning into an iteration
I will turn this function first into a tail recursion, then remove the recursive step and make it fully iterative.
To turn into tail recursion, just reverse steps 2 and 3. As stated above, only one variable is needed 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 exchange all this for a for, where the stop condition is the stop condition of 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 constant additional memory o(1), as opposed to the additional recursion memory that is o(nElementos). Time, however, continues in the same asymptotic behavior of o(nElementos).
Other alternatives?
At the moment, I think as another alternative to write in a size vector nElementos and copy back to the list nodes. Here we have an inversion of values, not addresses. As you are doing an exercise, I believe this will be the alternative that would give less points to you, but still doing the inversion (and I find a valid curiosity).
Note that it will be necessary to scroll through the list twice now, and that additional memory of o(nElementos).
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
}
}
then just create a recursive method that goes into the stack until the last position and start printing backwards (cc @Jeffersonquesado)
– Don't Panic
@Everson recursive, iterative, and brute force on an array. I’m finishing that last point more for completeness than necessity
– Jefferson Quesado