두 개의 포인터 만 사용하여 단일 연결 목록을 반전하는 방법은 무엇입니까?


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

연결 목록을 반대로 바꾸는 다른 대안이 있습니까? 시간 복잡성 측면에서 단일 연결 목록을 반전하는 가장 좋은 논리는 무엇입니까?



3
실제로는 두 개의 포인터가 아니라 두 개의 대기열입니다.
paxdiablo

7
당신이 도와 주러 온 거니까 rep 게임을하는 게 아니니?
GManNickG

1
GMan : 그게 문제 야. 내가 누군가를 돕고 있는지 잘 모르겠어.

1
당신은 질문과 답변에서 무언가를 읽고 얻는 우리를 돕고 있습니다. 나는 그것이 통찰력이 있다는 것을 알았습니다.
Andrew Coleson

답변:


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을 받는다. 그러나 당신이 '명백한 오류'라고 생각하는 것에 대한 설명은 당신의 대답을 향상시킬 것이다.
Jonathan Leffler

2
위 코드에 버그가 없나요? while 루프 내에서 매번 새로운 '다음'포인터를 생성합니다. 따라서 링크 된 목록에 N 개의 노드가있는 경우 N 개의 새 포인터를 만들고이를 해제하거나 삭제하지 않습니다. while 루프 앞에 'next'포인터를 만들고 while 루프 안에 'next = root-> next'를 할당하면 맞을 것이라고 생각합니다.
aks

6
@aks : 누출이 없습니다. malloc / etc에주의하십시오. 호출되지 않으므로 해제 할 필요가 없습니다. 변수 'next'는 루프로 범위가 지정되지만 완벽하게 괜찮습니다.

1
누수가없는 경우에도 aks가 언급했듯이 매번 다음에 선언 할 필요는 무엇입니까? "while 루프 앞에 'next'포인터를 만들고 'next = root-> next 'while 루프 안에 있습니다. ", 그렇지 않나요?
GeekyJ

1
나는 당신의 연결 목록 리터럴을 좋아합니다.

43

나는 나쁜 소식을 전하는 것이 싫지만 당신의 3 점식 해결책이 실제로 효과가 있다고 생각하지 않습니다. 다음 테스트 하네스에서 사용했을 때 다음 출력에 따라 목록이 하나의 노드로 축소되었습니다.

==========
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 개의 포인터가 여전히 필요합니다.
Kevin Kibler

먼저 curNode 및 nxtNode를 사용하고 있으며 총 세 개의 포인터를 사용합니다. 이것이 두 포인터 솔루션 인 이유는 무엇입니까?
Yashasvi 2014 년

@Yash, 다시이 읽을 여분 의 상단에 포인터를 first. 같은 방법은 영업 이익의 3 포인터 솔루션했다 first, p, qr.
paxdiablo

@paxdiablo 오! 내 잘못이야. 죄송합니다. 질문을 잘못 이해했습니다. 감사합니다 :)
Yashasvi 2014 년

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
설명
해주신

3

예. 세 번째를 사용하지 않고 두 개의 숫자를 바꿀 수 있는 것과 같은 방식으로이 작업을 수행 할 수 있습니다. . 포인터를 int / long으로 캐스팅하고 XOR 연산을 몇 번 수행하면됩니다. 이것은 재미있는 질문을 만드는 C 트릭 중 하나이지만 실질적인 가치는 없습니다.

O (n) 복잡성을 줄일 수 있습니까? 아니 정말. 역순이 필요하다고 생각되면 이중 연결 목록을 사용하십시오.


…주의하지 않으면 새로운 64 비트 호환성 문제가 발생합니다. 이런 식으로 성능을 구매할 가능성은 거의 없습니다.
LnxPrgr3

2
이것은 시간 복잡성에 영향을주지 않습니다. 즉, 선형 시간보다 솔루션을 더 좋게 만들지 않습니다 . 내 말은, 4 바이트 또는 8 바이트의 메모리를 절약 할 수 있지만 알고리즘의 전체적인 복잡성을 변경하지는 않습니다.
poundifdef

@rascher, 시간 복잡성이 질문 의 두 번째 부분이었습니다. 첫 번째 부분은 필요한 포인터 수를 줄이는 것과 관련이 있습니다.
paxdiablo

2
원래 포스터는 값싼 C 트릭을 찾고 있었다고 생각합니다. 내 경험상-그리고 나는 그것을 프로파일 링했습니다 :)-전형적인 피하는 중개 트릭은 실제로 단순히 중개자를 사용하는 것보다 느립니다.

링크가 끊어졌지만 XOR을 사용하여 2 개의 숫자를 바꾸는 것은 구식입니다. :)
Dane

3

Robert Sedgewick, " C의 알고리즘 ", Addison-Wesley, 3rd Edition, 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 컴파일러는 꼬리 호출 최적화를 "할 수 있습니다". 주어진 컴파일러 / 옵션이 있는지 여부에 관계없이 쉽게 확인할 수 있습니다. 디스 어셈블리를 살펴보세요. 또는 그것을 몇 백만 노드를 제공하고 ;-) 충돌이 있는지 확인
스티브 Jessop에게

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). 흥미 롭기는하지만이 방식으로 스와핑하는 것은 현대 시스템에서 차선책입니다.
ideasman42

3

목록을 추적 할 트랙 포인터 가 필요 합니다.

두 가지 포인터가 필요합니다.

번째 노드를 선택하는 첫 번째 포인터 . 번째 노드를 선택하는 두 번째 포인터 .

처리 :

트랙 포인터 이동

두 번째 노드를 첫 번째 노드로 지정

두 번째 포인터를 1에 할당하여 첫 번째 포인터를 한 단계 이동

두 번째 포인터를 한 단계 이동, 두 번째에 트랙 포인터를 할당하여

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

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 구현에 대해서는 저에게 연락하십시오.
Mr. Amit Kumar

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

나는 이것을 구현하기 위해 자바를 사용하고 있으며 접근 방식은 테스트 주도 개발이므로 테스트 케이스도 첨부됩니다.

단일 노드를 나타내는 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;
    }


}

그리고 위의 시나리오를 다루는 테스트 케이스. junit jar가 필요합니다. 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 개의 포인터 (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.
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;
}

이것이 어떻게 옳은가. 두 개 이상의 포인터를 사용하고 있으며 함수 호출을 할 때마다 스택에 숨겨져 있습니다.
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);

    }


}

node는 포인터가 아닙니다. 우리는 단지 head를 node로 전달했습니다. 더 자세한 설명이 필요하면 알려주세요
Raju Muke 2019 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.