Как перевернуть односвязный список, используя только два указателя?

109

Интересно, существует ли какая-то логика для изменения односвязного списка с использованием только двух указателей.

Ниже используется для изменения одного связанного списка с использованием трех указателей , а именно 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;
}

Есть ли другой способ изменить связанный список? Что было бы лучшей логикой для переворота односвязного списка с точки зрения временной сложности?

Мадхен
источник
1
возможный дубликат: stackoverflow.com/questions/818443/…
kajaco
3
Не совсем, это две очереди, а не два указателя.
paxdiablo
7
Потому что вы здесь, чтобы помочь, а не играть в репутацию?
GManNickG
1
GMan: в том-то и дело, я не уверен, что помогу кому-то, даже ему, если он не сможет довести дело до конца.
1
Вы помогаете тем из нас, кто читает и получает что-то из вопросов и ответов. Я нашел это проницательным.
Эндрю Колсон

Ответы:

133

Любая альтернатива? Нет, это настолько просто, насколько возможно, и принципиально другого способа сделать это не существует. Этот алгоритм уже работает O (n) раз, и вы не можете достичь большего, поскольку вы должны изменить каждый узел.

Похоже, ваш код находится на правильном пути, но он не совсем работает в приведенной выше форме. Вот рабочая версия:

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

источник
Я не уверен в «очевидных ошибках» в оригинале. С точки зрения дизайна, не передавать заголовок списка и не возвращать новый заголовок - плохая идея. Единственная ошибка reverse(), я считаю , что последняя строка в функции должна быть установлена ​​первой. В остальном исходный код работал нормально при подключении к аккуратной тестовой обвязке. Даже в этом случае вы получаете от меня +1, но объяснение того, что вы считаете «очевидными ошибками», улучшило бы ваш ответ.
Джонатан Леффлер,
2
Нет ли ошибки в приведенном выше коде? Внутри цикла while вы каждый раз создаете новый указатель «следующий». Таким образом, если в связанном списке есть N узлов, вы создаете N новых указателей и не освобождаете и не удаляете их. Я думаю, будет правильно, если вы создадите указатель 'next' перед циклом while и просто сделаете присвоение 'next = root-> next' внутри цикла while.
Акс
6
@aks: Утечки нет. Обратите внимание на malloc / etc. не называются, поэтому освобождать не нужно. Переменная next привязана к циклу, но это нормально.
1
Даже если утечки нет, зачем каждый раз объявлять в следующий раз, как уже упоминалось, «было бы правильно, если вы создадите указатель 'next' перед циклом while и просто выполните присвоение 'next = root-> next 'внутри цикла while ", не так ли?
GeekyJ
1
Мне нравятся ваши литералы связанного списка, это аккуратно.
43

Ненавижу сообщать плохие новости, но я не думаю, что ваше трехпозиционное решение действительно работает. Когда я использовал его в следующем тестовом пакете, список был сокращен до одного узла, как показано в следующем выводе:

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

Вы не получите более высокую временную сложность, чем ваше решение, так как это O (n), и вам нужно посетить каждый узел, чтобы изменить указатели, но вы можете довольно легко сделать решение только с двумя дополнительными указателями, как показано в следующем коде:

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

Этот код выводит:

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

что, я думаю, именно то, что вам нужно. Он действительно может это сделать, поскольку, как только вы загрузились firstв указатель, перемещающийся по списку, вы можете повторно использовать его firstпо своему желанию.

paxdiablo
источник
2
Очень элегантно. Повторное использование firstуказателя на самом связном список позволяет решению использовать только 2 дополнительные указатели, но 3 общих указатели по - прежнему необходимы для этого.
Кевин Киблер,
Вы используете first, curNode и nxtNode, всего три указателя для этого. почему это решение с двумя указателями?
Яшасви
@Yash, прочитай еще раз, два дополнительных указателя поверх first. Точно так же решение трехочковый в OP имела first, p, qи r.
paxdiablo
@paxdiablo ой! моя вина. Извините, я неправильно понял вопрос. Спасибо :)
Яшасви
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;
}
склеечный пресс
источник
2
Здравствуйте! Я знаю, что это старый вопрос, но не могли бы вы объяснить, что происходит в этой функции и почему она работает. :) Спасибо!
MakeTheTrumpetsBlow
13

Вот код , чтобы отменить однократно связанный список в C .

А вот и наклеено ниже:

// 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
источник
4
Спасибо за потрясающее искусство ASCII за объяснение :)
achedeuzot
3

Да. Я уверен, что вы можете сделать это так же, как поменять местами два числа, не используя третье . Просто приведите указатели к типу int / long и несколько раз выполните операцию XOR. Это один из тех приемов C, которые создают забавный вопрос, но не имеют практического значения.

Можете ли вы уменьшить сложность O (n)? Нет, не совсем. Просто используйте двусвязный список, если вы думаете, что вам понадобится обратный порядок.

Brianegge
источник
… И возникает новая проблема совместимости с 64-битными версиями, если вы не будете осторожны. Вы также вряд ли купите производительность таким образом.
LnxPrgr3
2
Это не повлияет на временную сложность, то есть не сделает решение лучше, чем линейное время. Я имею в виду, что вы можете сэкономить 4 или 8 байтов памяти, но это не изменит общую сложность алгоритма.
poundifdef
@rascher, временная сложность была второй частью вопроса. Первая часть была связана с уменьшением количества требуемых указателей.
paxdiablo
2
Я думаю, что исходный плакат искал дешевой уловки C. По моему опыту - и я это описал :) - типичные уловки, позволяющие избежать посредничества, на самом деле медленнее, чем просто использование посредника.
Will
Ссылка не работает, но я уверен, что поменять местами 2 числа с помощью XOR - это старая школа :)
датчанин,
3

Роберт Седжвик, « Алгоритмы на языке C », Addison-Wesley, 3-е издание, 1997 г., [раздел 3.4]

Если это не циклический список, значит, последняя ссылка - NULL.

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
источник
3

Просто для удовольствия (хотя оптимизация хвостовой рекурсии должна помешать ей съесть весь стек):


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

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

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

root = reverse(root, NULL);
Эндрю Джонсон
источник
2
Я думаю, что "следует" немного преувеличивает. Ваш компилятор C "может" выполнять оптимизацию хвостового вызова, и достаточно легко проверить, есть ли у данного компилятора / опций, есть ли это или нет: посмотрите на разборку. Или дайте ему несколько миллионов узлов и посмотрите, не выйдет ли он из строя ;-)
Стив Джессоп
3

Чтобы поменять местами две переменные без использования временной переменной,

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

самый быстрый способ - написать это в одну строку

a = a ^ b ^ (b=a)

Так же,

используя два свопа

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

решение с использованием xor

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

решение в одну строку

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

Та же логика используется для переворота связанного списка.

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
источник
1
Предполагается, что int имеет тот же размер, что и указатель, он не будет работать в системах amd64 (вы можете использовать intptr_t). Хотя интересно - такой способ подкачки неоптимален в современных системах.
ideasman42 07
3

Вам нужен указатель дорожки, который будет отслеживать список.

Вам понадобится два указателя:

первый указатель для выбора первого узла. второй указатель для выбора второго узла.

Обработка:

Указатель перемещения трека

Укажите второй узел на первый узел

Переместите первый указатель на один шаг, назначив второй указатель одному

Переместите второй указатель на один шаг, назначив указатель дорожки второму

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;

}

Сумит Арора
источник
2

Как насчет более читабельного:


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);
}
Эндрю Джонсон
источник
2

Вот более простая версия на Java. Он использует только два указателя curr&prev

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
}
Эрнесто
источник
Вопрос заключается в поиске решения на языке C, а не на Java
Degustaf
1
Вопрос скорее в том, чтобы выполнить обратную операцию только с двумя дополнительными указателями (или ссылками). Будь то C или Java, логика одинакова.
Эрнесто
1

Определите временную сложность алгоритма, который вы используете сейчас, и должно быть очевидно, что его нельзя улучшить.

Джон Ла Рой
источник
1

Я не понимаю, почему нужно возвращать голову, поскольку мы передаем ее в качестве аргумента. Мы передаем заголовок списка ссылок, затем мы также можем обновить. Ниже представлено простое решение.

#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;
}
Хардик Чаухан
источник
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;
}
Шридхар Гададе
источник
0

Нет, ничего быстрее текущего O (n) сделать нельзя. Вам нужно изменить каждый узел, поэтому время в любом случае будет пропорционально количеству элементов, а это O (n) у вас уже есть.

Sharptooth
источник
0

Использование двух указателей при сохранении временной сложности O (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);
}
Апшир
источник
0

У меня немного другой подход. Я хотел использовать существующие функции (например, insert_at (index), delete_from (index)), чтобы перевернуть список (что-то вроде операции сдвига вправо). Сложность по-прежнему O (n), но преимущество заключается в более частом повторном использовании кода. Взгляните на метод another_reverse () и дайте мне знать, что вы все думаете.

#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;
}
FunBoy
источник
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 ....

}

}
Амит Пури
источник
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);
}
Г-н Амит Кумар
источник
Свяжитесь со мной для любой проблемы с реализацией C.
Г-н Амит Кумар
0

Да, есть способ использовать только два указателя. То есть путем создания нового связанного списка, в котором первый узел является первым узлом данного списка, а второй узел первого списка добавляется в начало нового списка и так далее.

верма дипак
источник
0

Вот моя версия:

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

где

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
источник
0

Я использую java для реализации этого, и подход основан на разработке на основе тестов, поэтому тестовые примеры также прилагаются.

Класс Node, представляющий один узел -

package com.adnan.linkedlist;

/**
 * User  : Adnan
 * Email : sendtoadnan@gmail.com
 * 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;
    }
}

Класс обслуживания, который принимает начальный узел в качестве входных данных и резервирует его без использования дополнительного места.

package com.adnan.linkedlist;

/**
 * User  : Adnan
 * Email : sendtoadnan@gmail.com
 * 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;
    }


}

И тестовый пример, который охватывает вышеуказанный сценарий. Обратите внимание, что вам потребуются банки из джунита. Я использую testng.jar; вы можете использовать все, что вам нравится ..

package com.adnan.linkedlist;

import org.testng.annotations.Test;

import static org.testng.AssertJUnit.assertTrue;

/**
 * User  : Adnan
 * Email : sendtoadnan@gmail.com
 * 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();
        }


    }
}
Мохаммад Аднан
источник
0

Простой алгоритм, если вы используете связанный список как структуру стека:

 #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;

}

На производительность может повлиять дополнительный вызов функции для add и malloc, поэтому алгоритмы перестановки адресов лучше, но этот фактически создает новый список, поэтому вы можете использовать дополнительные параметры, такие как сортировка или удаление элементов, если вы добавляете функцию обратного вызова в качестве параметра в обеспечить регресс.

Илиан Запрянов
источник
0

Вот немного другой, но простой подход в 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;
}

Вывод здесь

деревенщина
источник
0

Ниже приведена одна реализация с использованием 2 указателей (голова и r).

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;
}
ARC
источник
Каким бы умным и неразборчивым он ни был, у вас проблемы, если sizeof(size_t) < sizeof(ListNode*)... вы должны использовать std::uintptr_t.
Quentin
0

вот небольшое простое решение ...

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;
        }       
   }
 }
Вакар Ахмед
источник
0

Вы можете решить эту проблему с помощью только одного дополнительного указателя, который должен быть статическим для обратной функции. Это в 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;
    }
}
Шайлендра Шривастав
источник
0

В качестве альтернативы вы можете использовать рекурсию -

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
источник
Как это правильно. Вы используете более двух указателей, они просто скрыты в стеке каждый раз, когда вы выполняете вызов функции.
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
Йостейн Топланд
источник
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);

    }


}
Раджу Муке
источник
узел не является указателем, мы просто передаем голову как узел. Дайте мне знать, если вам нужны дополнительные разъяснения
Раджу