จะย้อนกลับรายการที่เชื่อมโยงเดี่ยวโดยใช้ตัวชี้สองตัวได้อย่างไร


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
จีแมน: นั่นคือสิ่งนั้นฉันไม่แน่ใจว่ากำลังช่วยใครแม้แต่เขาถ้าเขาไม่สามารถทำตามได้

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 พอยน์เตอร์ใหม่และคุณจะไม่ว่างหรือลบออก ฉันคิดว่ามันจะถูกต้องถ้าคุณสร้างตัวชี้ 'next' ก่อนลูป while และทำการกำหนด 'next = root-> next' ภายในลูป while
aks

6
@aks: ไม่มีการรั่วไหล สังเกต malloc / ฯลฯ ไม่ได้ถูกเรียกจึงไม่จำเป็นต้องฟรี ตัวแปร 'ถัดไป' ถูกกำหนดขอบเขตไว้ที่ลูป แต่ก็ไม่เป็นไร

1
แม้ว่าจะไม่มีการรั่วไหลสิ่งที่จำเป็นในการประกาศครั้งต่อไปทุกครั้งตามที่ aks กล่าวไว้ "มันจะถูกต้องถ้าคุณสร้างตัวชี้ 'next' ก่อนลูป while และทำการกำหนด 'next = root-> next 'inside the while loop "ไม่ใช่เหรอ?
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 ตัว
Kevin Kibler

คุณกำลังใช้อันดับแรก curNode และ nxtNode รวมเป็นสามตัวชี้สำหรับสิ่งนี้ นี่เป็นโซลูชันตัวชี้สองตัวได้อย่างไร
Yashasvi

@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 ราคาถูก จากประสบการณ์ของฉัน - และฉันได้ทำโปรไฟล์ :) - เทคนิคการหลีกเลี่ยงตัวกลางโดยทั่วไปนั้นช้ากว่าการใช้ตัวกลางจริงๆ
Will

ลิงก์เสีย แต่ฉันแน่ใจว่าการสลับ 2 หมายเลขโดยใช้ XOR เป็นแบบเก่า :)
Dane

3

Robert Sedgewick, " Algorithms in C ", Addison-Wesley, 3rd Edition, 1997, [Section 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 ของคุณ "อาจ" ทำการเพิ่มประสิทธิภาพการโทรหางและง่ายพอที่จะตรวจสอบคอมไพลเลอร์ / ตัวเลือกที่กำหนดว่าทำได้หรือไม่: ดูที่การถอดชิ้นส่วน หรือให้มันสักสองสามล้านโหนดและดูว่ามันขัดข้องหรือไม่ ;-)
Steve 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

คุณต้องมีตัวชี้ติดตามซึ่งจะติดตามรายการ

คุณต้องมีตัวชี้สองตัว:

ตัวชี้แรก เพื่อเลือกโหนดแรก ตัวชี้ที่สองเพื่อเลือกโหนดที่สอง

การประมวลผล:

ย้ายตัวชี้ติดตาม

ชี้โหนดที่สองไปที่โหนดแรก

ย้ายตัวชี้แรกไปหนึ่งขั้นตอนโดยกำหนดตัวชี้ที่สองให้เป็นหนึ่ง

เลื่อนตัวชี้ที่สองไปหนึ่งขั้นตอนโดยกำหนดตัวชี้ติดตามให้เป็นวินาที

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 ตรรกะก็เหมือนกัน
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

ฉันใช้ 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;
    }


}

และกรณีทดสอบที่ครอบคลุมสถานการณ์ข้างต้น โปรดทราบว่าคุณต้องการขวด Junit ฉันใช้ 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);

    }


}

โหนดไม่ใช่ตัวชี้เราเพิ่งส่งหัวเป็นโหนด แจ้งให้เราทราบหากคุณต้องการคำชี้แจงเพิ่มเติม
Raju Muke
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.