Як змінити список, пов'язаний окремо, використовуючи лише два покажчики?


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 ви кожен раз створюєте новий вказівник "next". Отже, якщо у зв'язаному списку є N вузлів, ви створюєте N нових покажчиків, і ви не звільняєте їх та не видаляєте їх. Я думаю, що було б правильно, якщо ви створите вказівник «наступний» перед циклом while і просто зробите призначення «next = root-> next» всередині циклу while.
акс

6
@aks: Витоку немає. Помічайте malloc / тощо. не називаються, тому немає необхідності звільнятись. Змінна "next" розміщена в циклі, але це цілком нормально.

1
Навіть якщо немає протікання, у чому полягає потреба декларування наступного разу, як згадується АКС, "було б правильно, якщо ви створили вказівник" наступний "перед циклом while і просто зробите призначення" next = root-> next " "всередині циклу". ", чи не так?
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за бажанням.


2
Дуже елегантний. Повторне використання firstвказівника у зв'язаному списку дозволяє рішенню використовувати лише 2 додаткових вказівника, але для цього все ж необхідні 3 загальних вказівника.
Кевін Кіблер

Для цього ви використовуєте перший, curNode та nxtNode, загалом три покажчики. як так, це рішення з двома вказівниками?
Яшасві

@Yash, прочитайте ще раз, два додаткових вказівника поверх first. Точно так же рішення триочковий в OP мала first, p, qі r.
paxdiablo

@paxdiablo о! моє ліжко. Вибачте, я неправильно зрозумів питання. Дякую :)
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;
}

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

4
Дякую за приголомшливе мистецтво ASCII за пояснення :)
achedeuzot

3

Так. Я впевнений, що ви можете зробити це так само, як ви можете поміняти місцями два числа, не використовуючи третій . Просто киньте покажчики на int / long і виконайте операцію XOR пару разів. Це одна з тих хитрощів на C, яка робить цікавим питання, але не має жодної практичної цінності.

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


… І виникла нова 64-розрядна проблема сумісності, якщо ви не обережні. Ви навряд чи таким чином придбаєте будь-яку виставу.
LnxPrgr3

2
Це не вплине на часову складність - тобто не зробить рішення кращим, ніж лінійний час. Тобто, ви можете заощадити 4 або 8 байт пам'яті, але це не змінить загальної складності алгоритму.
poundifdef

@rascher, часова складність була другою частиною питання. Перша частина стосувалася зменшення кількості необхідних покажчиків.
paxdiablo

2
Я думаю, що оригінальний плакат шукав дешевий трюк C. На моєму досвіді - і я це профілював :) - типові ухилення від посередницьких хитрощів насправді повільніше, ніж просто використання посередника.
Буде

Посилання розірвано, але я впевнений, що поміняти 2 номери за допомогою XOR - це стара школа :)
Дат

3

Роберт Седжевік, " Алгоритми на С ", Аддісон-Веслі, 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; }


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

1
Це передбачає, що int має той же розмір, що і вказівник, він не буде працювати в системах amd64 (ви можете використовувати intptr_t). Хоча цікаво - обмін таким способом є недостатньо оптимальним для сучасних систем.
ideaman42

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, а не на Яві
Degustaf

1
Питання більше про те, щоб зробити зворотну операцію лише двома додатковими вказівниками (або посиланнями). Будь то його C або Java, логіка однакова.
ernesto

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).


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 (індекс), delete_from (індекс)), щоб змінити список (щось на зразок операції з правою зміною). Складність як і раніше O (n), але перевагою є більше повторне використання коду. Погляньте на метод іншого_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;
}

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

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;

}

На ефективність може вплинути з-за виклику додаткової функції для додавання та зйомки, тому алгоритми заміни адрес краще, але цей фактично створює новий список, щоб ви могли використовувати додаткові параметри, такі як сортування або видалення елементів, якщо ви додасте функцію зворотного виклику як параметр до зворотний.


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 покажчиків (head та 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;
}

Як розумний і нерозбірливий, як це може бути, ви переживаєте проблеми, якщо sizeof(size_t) < sizeof(ListNode*)... вам варто скористатися std::uintptr_t.
Квентін

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

Як це правильно. Ви використовуєте більше двох покажчиків, щойно приховані на стеку щоразу, коли ви виконуєте виклик функції.
Майк Г

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

    }


}

вузол не вказівник, у нас просто голова як вузол. Повідомте мене, якщо вам потрібно більше роз'яснень
Раджу Муке
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.