¿Cómo revertir una lista enlazada individualmente usando solo dos punteros?

109

Me pregunto si existe alguna lógica para revertir una lista enlazada individualmente usando solo dos punteros.

Se utiliza la siguiente para revertir la lista enlazada solo utilizando tres punteros a saber p, q, r:

struct node {
    int data;
    struct node *link;
};

void reverse() {
    struct node *p = first,
                *q = NULL,
                *r;

    while (p != NULL) {
        r = q;
        q = p;
        p = p->link;
        q->link = r;
    }
    first = q;
}

¿Existe alguna otra alternativa para revertir la lista enlazada? ¿Cuál sería la mejor lógica para invertir una lista enlazada individualmente, en términos de complejidad temporal?

Madhan
fuente
1
posible duplicado: stackoverflow.com/questions/818443/…
kajaco
3
En realidad no, son dos colas en lugar de dos punteros.
paxdiablo
7
¿Porque estás aquí para ayudar y no jugar un juego de repeticiones?
GManNickG
1
GMan: esa es la cuestión, no estoy seguro de estar ayudando a nadie, ni siquiera a él, si no puede seguir adelante.
1
Estás ayudando a aquellos de nosotros que leemos y obtenemos algo de las preguntas y respuestas. Lo encontré revelador.
Andrew Coleson

Respuestas:

133

¿Alguna alternativa? No, esto es tan simple como parece, y no hay una forma fundamentalmente diferente de hacerlo. Este algoritmo ya es O (n) tiempo, y no puede obtener más rápido que eso, ya que debe modificar cada nodo.

Parece que su código está en el camino correcto, pero no funciona en el formulario anterior. Aquí hay una versión funcional:

#include <stdio.h>

typedef struct Node {
  char data;
  struct Node* next;
} Node;

void print_list(Node* root) {
  while (root) {
    printf("%c ", root->data);
    root = root->next;
  }
  printf("\n");
}

Node* reverse(Node* root) {
  Node* new_root = 0;
  while (root) {
    Node* next = root->next;
    root->next = new_root;
    new_root = root;
    root = next;
  }
  return new_root;
}

int main() {
  Node d = { 'd', 0 };
  Node c = { 'c', &d };
  Node b = { 'b', &c };
  Node a = { 'a', &b };

  Node* root = &a;
  print_list(root);
  root = reverse(root);
  print_list(root);

  return 0;
}

fuente
No estoy seguro de los "errores obvios" del original. En cuanto al diseño, no pasar el encabezado de la lista y no devolver el nuevo encabezado es una mala idea. Sin embargo, el único error es que la última línea de la reverse()función debería configurarse primero, creo. De lo contrario, el código original funcionó bien cuando se conectó a su prolijo arnés de prueba. Aún así, obtienes +1 de mí, pero una explicación de lo que consideras los 'errores obvios' mejoraría tu respuesta.
Jonathan Leffler
2
¿No hay un error en el código anterior? Dentro del ciclo while, está creando un nuevo puntero "siguiente" cada vez. Entonces, si hay N nodos en la lista vinculada, está creando N nuevos punteros y no los está liberando ni eliminándolos. Creo que sería correcto si crea el puntero 'siguiente' antes del ciclo while y simplemente realiza la asignación 'siguiente = raíz-> siguiente' dentro del ciclo while.
aks
6
@aks: No hay fugas. Observe malloc / etc. no se llaman por lo que no hay necesidad de liberar. La variable 'siguiente' tiene como alcance el bucle, pero está perfectamente bien.
1
Incluso si no hay fugas, ¿cuál es la necesidad de declarar next cada vez, como mencionó aks, "sería correcto si crea el puntero 'siguiente' antes del ciclo while y simplemente hace la asignación 'next = root-> next 'dentro del bucle while ". ¿No es así?
GeekyJ
1
Me gustan los literales de su lista enlazada, eso es genial.
43

Odio ser portador de malas noticias, pero no creo que su solución de tres puntos realmente funcione. Cuando lo usé en el siguiente arnés de prueba, la lista se redujo a un nodo, según el siguiente resultado:

==========
4
3
2
1
0
==========
4
==========

No obtendrá una mejor complejidad de tiempo que su solución, ya que es O (n) y debe visitar cada nodo para cambiar los punteros, pero puede hacer una solución con solo dos punteros adicionales con bastante facilidad, como se muestra en el siguiente código:

#include <stdio.h>

// The list element type and head.

struct node { 
    int data;
    struct node *link;
};
static struct node *first = NULL;

// A reverse function which uses only two extra pointers.

void reverse() {
    // curNode traverses the list, first is reset to empty list.
    struct node *curNode = first, *nxtNode;
    first = NULL;

    // Until no more in list, insert current before first and advance.
    while (curNode != NULL) {
        // Need to save next node since we're changing the current.
        nxtNode = curNode->link;

        // Insert at start of new list.
        curNode->link = first;
        first = curNode;

        // Advance to next.
        curNode = nxtNode;
    }
}

// Code to dump the current list.

static void dumpNodes() {
    struct node *curNode = first;
    printf ("==========\n");
    while (curNode != NULL) {
        printf ("%d\n", curNode->data);
        curNode = curNode->link;
    }
}

// Test harness main program.

int main (void) {
    int i;
    struct node *newnode;

    // Create list (using actually the same insert-before-first
    // that is used in reverse function.

    for (i = 0; i < 5; i++) {
        newnode = malloc (sizeof (struct node));
        newnode->data = i;
        newnode->link = first;
        first = newnode;
    }

    // Dump list, reverse it, then dump again.

    dumpNodes();
    reverse();
    dumpNodes();
    printf ("==========\n");

    return 0;
}

Este código genera:

==========
4
3
2
1
0
==========
0
1
2
3
4
==========

que creo que es lo que buscabas. De hecho, puede hacer esto ya que, una vez que lo haya cargado firsten el puntero que atraviesa la lista, puede reutilizarlo firsta voluntad.

paxdiablo
fuente
2
Muy elegante. Reutilizar el firstpuntero en la lista vinculada permite que la solución use solo 2 punteros adicionales , pero aún son necesarios 3 punteros en total para esto.
Kevin Kibler
Está utilizando primero, curNode y nxtNode, un total de tres punteros para esto. ¿Cómo es que esta es una solución de dos punteros?
Yashasvi
@Yash, vuelve a leer, dos consejos adicionales además de first. La misma manera de la solución triple de la OP tenía first, p, qy r.
paxdiablo
@paxdiablo ¡oh! culpa mía. Lo siento, entendí mal la pregunta. Gracias :)
Yashasvi
25
#include <stddef.h>

typedef struct Node {
    struct Node *next;
    int data;
} Node;

Node * reverse(Node *cur) {
    Node *prev = NULL;
    while (cur) {
        Node *temp = cur;
        cur = cur->next; // advance cur
        temp->next = prev;
        prev = temp; // advance prev
    }
    return prev;
}
máquina de montaje
fuente
2
¡Hola! Sé que esta pregunta es antigua, pero ¿le importaría explicar qué sucede en esta función y por qué funciona? :) ¡Gracias!
MakeTheTrumpetsBlow
13

Aquí está el código para revertir una lista enlazada en C .

Y aquí está pegado a continuación:

// reverse.c

#include <stdio.h>
#include <assert.h>

typedef struct node Node;
struct node {
    int data;
    Node *next;
};

void spec_reverse();
Node *reverse(Node *head);

int main()
{
    spec_reverse();
    return 0;
}

void print(Node *head) {
    while (head) {
        printf("[%d]->", head->data);
        head = head->next;
    }
    printf("NULL\n");
}

void spec_reverse() {
    // Create a linked list.
    // [0]->[1]->[2]->NULL
    Node node2 = {2, NULL};
    Node node1 = {1, &node2};
    Node node0 = {0, &node1};
    Node *head = &node0;

    print(head);
    head = reverse(head);
    print(head);

    assert(head == &node2);
    assert(head->next == &node1);
    assert(head->next->next == &node0);

    printf("Passed!");
}

// Step 1:
//
// prev head  next
//   |    |    |
//   v    v    v
// NULL  [0]->[1]->[2]->NULL
//
// Step 2:
//
//      prev head  next
//        |    |    |
//        v    v    v
// NULL<-[0]  [1]->[2]->NULL
//
Node *reverse(Node *head)
{
    Node *prev = NULL;
    Node *next;

    while (head) {
        next = head->next;
        head->next = prev;
        prev = head;
        head = next;
    }

    return prev;
}
ma11hew28
fuente
4
Gracias por el impresionante arte ASCII por explicar :)
achedeuzot
3

Si. Estoy seguro de que puede hacer esto de la misma manera que puede intercambiar dos números sin usar un tercero . Simplemente eche los punteros a int / long y realice la operación XOR un par de veces. Este es uno de esos trucos de C que lo convierte en una pregunta divertida, pero no tiene ningún valor práctico.

¿Puede reducir la complejidad O (n)? No en realidad no. Solo use una lista doblemente vinculada si cree que va a necesitar el orden inverso.

Brianegge
fuente
... y nace un nuevo problema de compatibilidad de 64 bits, si no tiene cuidado. Es poco probable que compres alguna actuación de esta manera.
LnxPrgr3
2
Esto no afectará la complejidad del tiempo, es decir, no hará que la solución sea mejor que el tiempo lineal. Quiero decir, puede ahorrar 4 u 8 bytes de memoria, pero eso no cambiará la complejidad general del algoritmo.
poundifdef
@rascher, la complejidad del tiempo fue la segunda parte de la pregunta. La primera parte tenía que ver con la reducción del número de punteros necesarios.
paxdiablo
2
Creo que el póster original buscaba un truco C barato. En mi experiencia, y lo he perfilado :), los trucos típicos para evitar intermediarios son en realidad más lentos que simplemente usar un intermediario.
Será el
El enlace está roto, pero estoy seguro de que intercambiar 2 números usando XOR es de la vieja escuela :)
Dane
3

Robert Sedgewick, " Algoritmos en C ", Addison-Wesley, 3ª edición, 1997, [Sección 3.4]

En caso de que no sea una lista cíclica, NULL es el último enlace.

typedef struct node* link;

struct node{ int item; link next; };

/* you send the existing list to reverse() and returns the reversed one */

link reverse(link x){ link t, y = x, r = NULL; while(y != NULL){ t = y->next; y-> next = r; r = y; y = t; } return r; }

limitcracker
fuente
3

Solo por diversión (aunque la optimización de recursividad de cola debería evitar que se coma toda la pila):


Node* reverse (Node *root, Node *end) {

    Node *next = root->next;
    root->next = end;

    return (next ? reverse(next, root) : root);
}

root = reverse(root, NULL);
Andrew Johnson
fuente
2
Creo que "should" está exagerando un poco el caso. Su compilador de C "podría" hacer una optimización de llamada final, y es bastante fácil verificar un compilador / opciones dados, ya sea que lo haga o no: mire el desmontaje. O dale algunos millones de nodos y mira si falla ;-)
Steve Jessop
3

Para intercambiar dos variables sin el uso de una variable temporal,

a = a xor b
b = a xor b
a = a xor b

la forma más rápida es escribirlo en una línea

a = a ^ b ^ (b=a)

Similar,

usando dos intercambios

swap(a,b)
swap(b,c)

solución usando xor

a = a^b^c
b = a^b^c
c = a^b^c
a = a^b^c

solución en una línea

c = a ^ b ^ c ^ (a=b) ^ (b=c)
b = a ^ b ^ c ^ (c=a) ^ (a=b)
a = a ^ b ^ c ^ (b=c) ^ (c=a)

Se utiliza la misma lógica para invertir una lista enlazada.

typedef struct List
{
 int info;
 struct List *next;
}List;


List* reverseList(List *head)
{
 p=head;
 q=p->next;
 p->next=NULL;
 while(q)
 {
    q = (List*) ((int)p ^ (int)q ^ (int)q->next ^ (int)(q->next=p) ^ (int)(p=q));
 }
 head = p;
 return head;
}  
sr01853
fuente
1
Esto supone que un int es del mismo tamaño que un puntero, no funcionará en sistemas amd64 (podría usarlo intptr_t). Si bien es interesante, cambiar de esta manera no es óptimo en los sistemas modernos.
ideasman42
3

Necesita un puntero de pista que rastreará la lista.

Necesitas dos consejos:

primer puntero para elegir el primer nodo. segundo puntero para elegir el segundo nodo.

Procesando :

Mover puntero de pista

Apunte el segundo nodo al primer nodo

Mueva el primer puntero un paso, asignando el segundo puntero a uno

Mover el segundo puntero un paso, asignando el puntero de pista al segundo

Node* reverselist( )
{
   Node *first = NULL;  // To keep first node
   Node *second = head; // To keep second node
   Node *track =  head; // Track the list

    while(track!=NULL)
    {
      track = track->next; // track point to next node;
      second->next = first; // second node point to first
      first = second; // move first node to next
      second = track; // move second node to next
    }

    track = first;

    return track;

}

Sumit Arora
fuente
2

¿Qué tal el más legible?


Node *pop (Node **root)
{
    Node *popped = *root;

    if (*root) {
        *root = (*root)->next;
    }

    return (popped);
}

void push (Node **root, Node *new_node)
{
    new_node->next = *root;
    *root = new_node;
}


Node *reverse (Node *root)
{
    Node *new_root = NULL;
    Node *next;

    while ((next = pop(&root))) {
        push (&new_root, next);
    }

    return (new_root);
}
Andrew Johnson
fuente
2

Aquí hay una versión más simple en Java. Utiliza solo dos punteros curryprev

public void reverse(Node head) {
    Node curr = head, prev = null;

    while (head.next != null) {
        head = head.next; // move the head to next node
        curr.next = prev; //break the link to the next node and assign it to previous
        prev = curr;      // we are done with previous, move it to next node
        curr = head;      // current moves along with head
    }

    head.next = prev;     //for last node
}
ernesto
fuente
La pregunta es buscar una solución C, no una en Java
Degustaf
1
La pregunta es más sobre hacer la operación inversa con solo dos punteros (o referencias) adicionales. Ya sea en C o Java, la lógica es la misma.
ernesto
1

Calcule la complejidad temporal del algoritmo que está utilizando ahora y debería ser obvio que no se puede mejorar.

John La Rooy
fuente
1

No entiendo por qué es necesario devolver la cabeza ya que la estamos pasando como argumento. Estamos pasando el encabezado de la lista de enlaces y luego podemos actualizar también. A continuación se muestra una solución simple.

#include<stdio.h>
#include<conio.h>

struct NODE
{
    struct NODE *next;
    int value;
};

typedef struct NODE node;

void reverse(node **head);
void add_end(node **head,int val);
void alloc(node **p);
void print_all(node *head);

void main()
{
    node *head;
    clrscr();
    head = NULL;
    add_end( &head, 1 );
    add_end( &head, 2 );
    add_end( &head, 3 );
    print_all( head );
    reverse( &head );
    print_all( head );
    getch();
}
void alloc(node **p)
{
    node *temp;
    temp = (node *) malloc( sizeof(node *) );
    temp->next = NULL;
    *p = temp;
}
void add_end(node **head,int val)
{
    node *temp,*new_node;
    alloc(&new_node);
    new_node->value = val;
    if( *head == NULL )
    {
        *head = new_node;
        return;
    }
    for(temp = *head;temp->next!=NULL;temp=temp->next);
    temp->next = new_node;
}
void print_all(node *head)
{
    node *temp;
    int index=0;
    printf ("\n\n");
    if (head == NULL)
    {
        printf (" List is Empty \n");
        return;
    }
    for (temp=head; temp != NULL; temp=temp->next,index++)
        printf (" %d ==> %d \n",index,temp->value);
}
void reverse(node **head)
{
    node *next,*new_head;
    new_head=NULL;
    while(*head != NULL)
    {
        next = (*head)->next;
        (*head)->next = new_head;
        new_head = (*head);
        (*head) = next;
    }
    (*head)=new_head;
}
Hardik Chauhan
fuente
1
#include <stdio.h>
#include <malloc.h>

tydef struct node
{
    int info;
    struct node *link;
} *start;

void main()
{
    rev();
}

void rev()
{
    struct node *p = start, *q = NULL, *r;
    while (p != NULL)
    {
        r = q;
        q = p;
        p = p->link;
        q->link = r;
    }

    start = q;
}
Shridhar Gadade
fuente
0

No, no se puede hacer nada más rápido que el actual O (n). Necesita alterar cada nodo, por lo que el tiempo será proporcional al número de elementos de todos modos y eso es O (n) que ya tiene.

diente filoso
fuente
0

El uso de dos punteros mientras se mantiene la complejidad de tiempo de O (n), el más rápido que se puede lograr, solo podría ser posible mediante el lanzamiento de números de punteros e intercambiando sus valores. Aquí hay una implementación:

#include <stdio.h>

typedef struct node
{
    int num;
    struct node* next;
}node;

void reverse(node* head)
{
   node* ptr;
   if(!head || !head->next || !head->next->next) return;
   ptr = head->next->next;
   head->next->next = NULL;
   while(ptr)
   {
     /* Swap head->next and ptr. */
     head->next = (unsigned)(ptr =\
     (unsigned)ptr ^ (unsigned)(head->next =\
     (unsigned)head->next ^ (unsigned)ptr)) ^ (unsigned)head->next;

     /* Swap head->next->next and ptr. */
     head->next->next = (unsigned)(ptr =\
     (unsigned)ptr ^ (unsigned)(head->next->next =\
     (unsigned)head->next->next ^ (unsigned)ptr)) ^ (unsigned)head->next->next;
   }
}

void add_end(node* ptr, int n)
{
    while(ptr->next) ptr = ptr->next;
    ptr->next = malloc(sizeof(node));
    ptr->next->num = n;
    ptr->next->next = NULL;
}

void print(node* ptr)
{
    while(ptr = ptr->next) printf("%d ", ptr->num);
    putchar('\n');
}

void erase(node* ptr)
{
    node *end;
    while(ptr->next)
    {
        if(ptr->next->next) ptr = ptr->next;
        else
        {
            end = ptr->next;
            ptr->next = NULL;
            free(end);
        }
    }
}

void main()
{
    int i, n = 5;
    node* dummy_head;
    dummy_head->next = NULL;
    for(i = 1; i <= n ; ++i) add_end(dummy_head, i);
    print(dummy_head);
    reverse(dummy_head);
    print(dummy_head);
    erase(dummy_head);
}
Apshir
fuente
0

Tengo un enfoque ligeramente diferente. Quería hacer uso de las funciones existentes (como insert_at (index), delete_from (index)) para invertir la lista (algo así como una operación de desplazamiento a la derecha). La complejidad sigue siendo O (n) pero la ventaja es un código más reutilizado. Eche un vistazo al método another_reverse () y déjeme saber lo que piensa.

#include <stdio.h>
#include <stdlib.h>

struct node {
    int data;
    struct node* next;
};

struct node* head = NULL;

void printList(char* msg) {
    struct node* current = head;

    printf("\n%s\n", msg);

    while (current != NULL) {
        printf("%d ", current->data);
        current = current->next;
    }
}

void insert_beginning(int data) {
    struct node* newNode = (struct node*) malloc(sizeof(struct node));

    newNode->data = data;
    newNode->next = NULL;

    if (head == NULL)
    {
        head = newNode;
    } else {
        newNode->next = head;
        head = newNode;
    }
}

void insert_at(int data, int location) {

    struct node* newNode = (struct node*) malloc(sizeof(struct node));

    newNode->data = data;
    newNode->next = NULL;

    if (head == NULL)
    {
        head = newNode;
    }

    else {
        struct node* currentNode = head;
        int index = 0;

        while (currentNode != NULL && index < (location - 1)) {
            currentNode = currentNode->next;
            index++;
        }

        if (currentNode != NULL)
        {
            if (location == 0) {
                newNode->next = currentNode;
                head = newNode;
            } else {
                newNode->next = currentNode->next;
                currentNode->next = newNode;
            }
        }
    }
}


int delete_from(int location) {

    int retValue = -1;

    if (location < 0 || head == NULL)
    {
        printf("\nList is empty or invalid index");
        return -1;
    } else {

        struct node* currentNode = head;
        int index = 0;

        while (currentNode != NULL && index < (location - 1)) {
            currentNode = currentNode->next;
            index++;
        }

        if (currentNode != NULL)
        {
            // we've reached the node just one prior to the one we want to delete

            if (location == 0) {

                if (currentNode->next == NULL)
                {
                    // this is the only node in the list
                    retValue = currentNode->data;
                    free(currentNode);
                    head = NULL;
                } else {

                    // the next node should take its place
                    struct node* nextNode = currentNode->next;
                    head = nextNode;
                    retValue = currentNode->data;
                    free(currentNode);
                }
            } // if (location == 0)
            else {
                // the next node should take its place
                struct node* nextNode = currentNode->next;
                currentNode->next = nextNode->next;

                if (nextNode != NULL
                ) {
                    retValue = nextNode->data;
                    free(nextNode);
                }
            }

        } else {
            printf("\nInvalid index");
            return -1;
        }
    }

    return retValue;
}

void another_reverse() {
    if (head == NULL)
    {
        printf("\nList is empty\n");
        return;
    } else {
        // get the tail pointer

        struct node* tailNode = head;
        int index = 0, counter = 0;

        while (tailNode->next != NULL) {
            tailNode = tailNode->next;
            index++;
        }

        // now tailNode points to the last node
        while (counter != index) {
            int data = delete_from(index);
            insert_at(data, counter);
            counter++;
        }
    }
}

int main(int argc, char** argv) {

    insert_beginning(4);
    insert_beginning(3);
    insert_beginning(2);
    insert_beginning(1);
    insert_beginning(0);

    /*  insert_at(5, 0);
     insert_at(4, 1);
     insert_at(3, 2);
     insert_at(1, 1);*/

    printList("Original List\0");

    //reverse_list();
    another_reverse();

    printList("Reversed List\0");

    /*  delete_from(2);
     delete_from(2);*/

    //printList();
    return 0;
}
Chico divertido
fuente
0
using 2-pointers....bit large but simple and efficient

void reverse()

{

int n=0;

node *temp,*temp1;

temp=strptr;

while(temp->next!=NULL)

{

n++;      //counting no. of nodes

temp=temp->next;

}
// we will exchange ist by last.....2nd by 2nd last so.on....
int i=n/2;  

temp=strptr;

for(int j=1;j<=(n-i+1);j++)

temp=temp->next;
//  i started exchanging from in between ....so we do no have to traverse list so far //again and again for exchanging

while(i>0)

{

temp1=strptr;

for(int j=1;j<=i;j++)//this loop for traversing nodes before n/2

temp1=temp1->next;

int t;

t=temp1->info;

temp1->info=temp->info;

temp->info=t;

i--;

temp=temp->next; 

//at the end after exchanging say 2 and 4 in a 5 node list....temp will be at 5 and we will traverse temp1 to ist node and exchange ....

}

}
Amit Puri
fuente
0
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
struct node
{
int data;
struct node *link;
};
struct node *first=NULL,*last=NULL,*next,*pre,*cur,*temp;
void create()
{
cur=(struct node*) malloc(sizeof(struct node));
printf("enter first data to insert");
scanf("%d",&cur->data);
first=last=cur;
first->link=NULL;
}
void insert()
{
int pos,c;
cur=(struct node*) malloc(sizeof(struct node));
printf("enter data to insert and also its position");
scanf("%d%d",&cur->data,&pos);
if(pos==1)
{
cur->link=first;
first=cur;
}
else
{
c=1;
    next=first;
    while(c<pos)
    {
        pre=next;
        next=next->link;
        c++;
    }
        if(pre==NULL)
        {
            printf("Invalid position");
        }
        else
        {
        cur->link=pre->link;
        pre->link=cur;
        }
}
}
void display()
{
cur=first;
while(cur!=NULL)
{
printf("data= %d\t address= %u\n",cur->data,cur);
cur=cur->link;
}
printf("\n");
}
void rev()
{
pre=NULL;
cur=first;
while(cur!=NULL)
{
next=cur->link;
cur->link=pre;
pre=cur;
cur=next;
}
first=pre;
}
void main()
{
int choice;
clrscr();
do
{
printf("Options are: -\n1:Create\n2:Insert\n3:Display\n4:Reverse\n0:Exit\n");
printf("Enter your choice: - ");
scanf("%d",&choice);
switch(choice)
{
case 1:
create();
break;
case 2:
insert();
break;
case 3:
display();
break;
case 4:
rev();
break;
case 0:
exit(0);
default:
printf("wrong choice");
}
}
while(1);
}
Sr. Amit Kumar
fuente
Contácteme para la implementación de C de cualquier problema.
Sr. Amit Kumar
0

Sí, hay una forma de usar solo dos punteros. Es decir, creando una nueva lista vinculada donde el primer nodo es el primer nodo de la lista dada y el segundo nodo de la primera lista se agrega al comienzo de la nueva lista y así sucesivamente.

deepak verma
fuente
0

Aquí está mi versión:

void reverse(ListElem *&head)
{
    ListElem* temp;
    ListElem* elem = head->next();
    ListElem* prev = head;
    head->next(0);

    while(temp = elem->next())
    {
        elem->next(prev);
        prev = elem;
        elem = temp;
    }
    elem->next(prev);
    head = elem;
}

dónde

class ListElem{
public:
    ListElem(int val): _val(val){}
    ListElem *next() const { return _next; }
    void next(ListElem *elem) { _next = elem; }
    void val(int val){ _val = val; }
    int val() const { return _val;}
private:
    ListElem *_next;
    int _val;
};
cpp
fuente
0

Estoy usando Java para implementar esto y el enfoque es un desarrollo impulsado por pruebas, por lo que también se adjuntan casos de prueba.

La clase Node que representa un solo nodo -

package com.adnan.linkedlist;

/**
 * User  : Adnan
 * Email : [email protected]
 * Date  : 9/21/13
 * Time  : 12:02 PM
 */
public class Node {

    public Node(int value, Node node){
        this.value = value;
        this.node = node;
    }
    private int value;
    private Node node;

    public int getValue() {
        return value;
    }

    public Node getNode() {
        return node;
    }

    public void setNode(Node node){
        this.node = node;
    }
}

Clase de servicio que toma el nodo de inicio como entrada y lo reserva sin usar espacio adicional.

package com.adnan.linkedlist;

/**
 * User  : Adnan
 * Email : [email protected]
 * Date  : 9/21/13
 * Time  : 11:54 AM
 */
public class SinglyLinkedListReversal {

    private static final SinglyLinkedListReversal service 
= new SinglyLinkedListReversal();
    public static SinglyLinkedListReversal getService(){
        return service;
    }



    public Node reverse(Node start){
        if (hasOnlyNodeInLinkedList(start)){
            return start;
        }
        Node firstNode, secondNode, thirdNode;
        firstNode = start;
        secondNode = firstNode.getNode();
        while (secondNode != null ){
            thirdNode = secondNode.getNode();
            secondNode.setNode(firstNode);
            firstNode = secondNode;
            secondNode = thirdNode;
        }
        start.setNode(null);
        return firstNode;
    }

    private boolean hasOnlyNodeInLinkedList(Node start) {
        return start.getNode() == null;
    }


}

Y el caso de prueba que cubre el escenario anterior. Tenga en cuenta que necesita frascos junit. Estoy usando testng.jar; puedes usar cualquier cosa que te plazca ..

package com.adnan.linkedlist;

import org.testng.annotations.Test;

import static org.testng.AssertJUnit.assertTrue;

/**
 * User  : Adnan
 * Email : [email protected]
 * Date  : 9/21/13
 * Time  : 12:11 PM
 */
public class SinglyLinkedListReversalTest {

    private SinglyLinkedListReversal reversalService = 
SinglyLinkedListReversal.getService();

    @Test
    public void test_reverseSingleElement() throws Exception {
        Node node = new Node(1, null);
        reversalService.reverse(node);
        assertTrue(node.getNode() == null);
        assertTrue(node.getValue() == 1);
    }


    //original - Node1(1) -> Node2(2) -> Node3(3)
    //reverse - Node3(3) -> Node2(2) -> Node1(1)
    @Test
    public void test_reverseThreeElement() throws Exception {
        Node node3 = new Node(3, null);
        Node node2 = new Node(2, node3);
        Node start = new Node(1, node2);


        start = reversalService.reverse(start);
        Node test = start;
        for (int i = 3; i >=1 ; i -- ){
          assertTrue(test.getValue() == i);
            test = test.getNode();
        }


    }

    @Test
    public void test_reverseFourElement() throws Exception {
        Node node4 = new Node(4, null);
        Node node3 = new Node(3, node4);
        Node node2 = new Node(2, node3);
        Node start = new Node(1, node2);


        start = reversalService.reverse(start);
        Node test = start;
        for (int i = 4; i >=1 ; i -- ){
            assertTrue(test.getValue() == i);
            test = test.getNode();
        }
    }

        @Test
        public void test_reverse10Element() throws Exception {
            Node node10 = new Node(10, null);
            Node node9 = new Node(9, node10);
            Node node8 = new Node(8, node9);
            Node node7 = new Node(7, node8);
            Node node6 = new Node(6, node7);
            Node node5 = new Node(5, node6);
            Node node4 = new Node(4, node5);
            Node node3 = new Node(3, node4);
            Node node2 = new Node(2, node3);
            Node start = new Node(1, node2);


            start = reversalService.reverse(start);
            Node test = start;
            for (int i = 10; i >=1 ; i -- ){
                assertTrue(test.getValue() == i);
                test = test.getNode();
            }


    }

    @Test
    public void test_reverseTwoElement() throws Exception {
        Node node2 = new Node(2, null);
        Node start = new Node(1, node2);


        start = reversalService.reverse(start);
        Node test = start;
        for (int i = 2; i >=1 ; i -- ){
            assertTrue(test.getValue() == i);
            test = test.getNode();
        }


    }
}
Mohammad Adnan
fuente
0

Un algoritmo simple si usa la lista vinculada como una estructura de pila:

 #include <stdio.h>
#include <stdlib.h>

typedef struct list {
    int key;
    char value;
    struct list* next;
} list;
void print(list*);
void add(list**, int, char);
void reverse(list**);
void deleteList(list*);

int main(void) {
    list* head = NULL;
    int i=0;
    while ( i++ < 26 ) add(&head, i, i+'a');
    printf("Before reverse: \n");
    print(head);
    printf("After reverse: \n");
    reverse(&head);
    print(head);
    deleteList(head);

}
void deleteList(list* l) {

    list* t = l;    
    while ( t != NULL ) {
        list* tmp = t;
        t = t->next;
        free(tmp);
    }

}
void print(list* l) {
    list* t = l;
    while ( t != NULL) {
        printf("%d:%c\n", t->key, t->value);
        t = t->next;
    }
}

void reverse(list** head) {
    list* tmp = *head;
    list* reversed = NULL;
    while ( tmp != NULL ) {
        add(&reversed, tmp->key, tmp->value);
        tmp = tmp->next;
    }
    deleteList(*head);
    *head = reversed;
}

void add(list** head, int k, char v) {

    list* t = calloc(1, sizeof(list));
    t->key = k; t->value = v;
    t->next = *head;
    *head = t;

}

El rendimiento puede verse afectado debido a la llamada de función adicional a add y malloc, por lo que los algoritmos de intercambio de direcciones son mejores, pero ese realmente crea una nueva lista para que pueda usar opciones adicionales como ordenar o eliminar elementos si agrega una función de devolución de llamada como parámetro al contrarrestar.

Ilian Zapryanov
fuente
0

Aquí hay un enfoque simple pero ligeramente diferente en C ++ 11:

#include <iostream>

struct Node{
    Node(): next(NULL){}
    Node *next;
    std::string data;
};

void printlist(Node* l){
    while(l){
        std::cout<<l->data<<std::endl;
        l = l->next;
    }
    std::cout<<"----"<<std::endl;
}

void reverse(Node*& l)
{
    Node* prev = NULL;
    while(l){
        auto next = l->next;
        l->next = prev;
        prev=l;
        l=next;
    }
    l = prev;
}

int main() {
    Node s,t,u,v;
    s.data = "1";
    t.data = "2";
    u.data = "3";
    v.data = "4";
    s.next = &t;
    t.next = &u;
    u.next = &v;
    Node* ptr = &s;
    printlist(ptr);
    reverse(ptr);
    printlist(ptr);
    return 0;
}

Salida aquí

Carl
fuente
0

A continuación se muestra una implementación con 2 punteros (head yr)

ListNode * reverse(ListNode* head) {

    ListNode *r = NULL;

    if(head) {
        r = head->next;
        head->next = NULL;
    }

    while(r) {
        head = reinterpret_cast<ListNode*>(size_t(head) ^ size_t(r->next));
        r->next = reinterpret_cast<ListNode*>(size_t(r->next) ^ size_t(head));
        head = reinterpret_cast<ListNode*>(size_t(head) ^ size_t(r->next));

        head = reinterpret_cast<ListNode*>(size_t(head) ^ size_t(r));
        r = reinterpret_cast<ListNode*>(size_t(r) ^ size_t(head));
        head = reinterpret_cast<ListNode*>(size_t(head) ^ size_t(r));
    }
    return head;
}
ARCO
fuente
Por más inteligente e indescifrable que sea, estás en problemas si sizeof(size_t) < sizeof(ListNode*)... debes usar std::uintptr_t.
Quentin
0

aquí hay una pequeña solución simple ...

void reverse()
{
    node * pointer1 = head->next;
    if(pointer1 != NULL)
    {
        node *pointer2 = pointer1->next;
        pointer1->next = head;
        head->next = NULL;
        head = pointer1;

        if(pointer2 != NULL)
        {

            while(pointer2 != NULL)
            {
                pointer1 = pointer2;
                pointer2 = pointer2->next;
                pointer1->next = head;
                head = pointer1;
            }

            pointer1->next = head;
            head = pointer1;
        }       
   }
 }
waqar ahmed
fuente
0

Puede solucionar este problema con la ayuda de un solo puntero adicional, que debe ser estático para la función inversa. Está en complejidad O (n).

#include<stdio.h>
#include<stdlib.h>

typedef struct List* List;
struct List {
   int val;
   List next;
};

List reverse(List list) { /* with recursion and one static variable*/
    static List tail;
    if(!list || !list->next) {
        tail = list;

        return tail;
    } else {
        reverse1(list->next);
        list->next->next = list;
        list->next = NULL;

        return tail;
    }
}
Shailendra Shrivastav
fuente
0

Como alternativa, puede utilizar la recursividad

struct node* reverseList(struct node *head)
{
    if(head == NULL) return NULL;
    if(head->next == NULL) return head;

    struct node* second = head->next;       
    head->next = NULL;

    struct node* remaining = reverseList(second);
    second->next = head;

    return remaining;
}
vaibhav
fuente
¿Cómo es esto correcto? Está utilizando más de dos punteros, simplemente se oculta en la pila cada vez que realiza una llamada a una función.
Mike G
0
curr = head;
prev = NULL;

while (curr != NULL) {
    next = curr->next; // store current's next, since it will be overwritten
    curr->next = prev;
    prev = curr;
    curr = next;
}

head = prev; // update head
Jostein Topland
fuente
0
class Node {
    Node next;
    int data;

    Node(int item) {
        data = item;
        next = null;
    }
}

public class LinkedList {

    static Node head;

    //Print LinkedList
    public static void printList(Node node){

        while(node!=null){
            System.out.print(node.data+" ");
            node = node.next;
        }
        System.out.println();
    }

    //Reverse the LinkedList Utility
    public static Node reverse(Node node){

        Node new_node = null;

        while(node!=null){

            Node next = node.next;
            node.next = new_node;
            new_node = node;
            node = next;

        }
        return new_node;
    }

    public static void main(String[] args) {

        //Creating LinkedList
        LinkedList.head = new Node(1);
        LinkedList.head.next = new Node(2);
        LinkedList.head.next.next = new Node(3);
        LinkedList.head.next.next.next = new Node(4);

        LinkedList.printList(LinkedList.head);

        Node node = LinkedList.reverse(LinkedList.head);

        LinkedList.printList(node);

    }


}
Raju Muke
fuente
node no es un puntero, solo tenemos que pasar head como node. Avísame si necesitas más aclaraciones
Raju Muke