如何仅使用两个指针反转单链表?

Mad*_*han 109 c algorithm linked-list data-structures singly-linked-list

我想知道是否存在一些逻辑来仅使用两个指针来反转链表.

以下用于使用三个指针(即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;
}
Run Code Online (Sandbox Code Playgroud)

还有其他替代方法来反转链表吗?在时间复杂度方面,逆转单链表的最佳逻辑是什么?

小智 131

还有其他选择 不,这很简单,并没有根本不同的方式.这个算法已经是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;
}
Run Code Online (Sandbox Code Playgroud)

  • @aks:没有泄漏.注意malloc/etc.没有被叫,所以没有必要自由.变量'next'的范围是循环,但这完全没问题. (6认同)
  • 上面的代码中是不是有错误?在while循环中,每次都会创建一个新的"下一个"指针.因此,如果链表中有N个节点,则表示您正在创建N个新指针,并且您不会释放或删除它们.我认为如果你在while循环之前创建'next'指针并且只是在while循环中进行赋值'next = root-> next'将是正确的. (2认同)

pax*_*blo 44

我不想成为坏消息的承载者,但我不认为你的三指针解决方案确实有效.当我在以下测试工具中使用它时,根据以下输出将列表缩减为一个节点:

==========
4
3
2
1
0
==========
4
==========
Run Code Online (Sandbox Code Playgroud)

你不会比你的解决方案获得更好的时间复杂度,因为它是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;
}
Run Code Online (Sandbox Code Playgroud)

此代码输出:

==========
4
3
2
1
0
==========
0
1
2
3
4
==========
Run Code Online (Sandbox Code Playgroud)

我认为你是追求的.它实际上可以这样做,因为一旦你加载first到遍历列表的指针,你可以随意重用first.

  • 十分优雅.重用链表上的`first`指针允许解决方案只使用2***指针,但仍需要3***指针. (2认同)

spl*_*cer 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 你好!我知道这个问题已经过时了,但是你能不能解释一下这个函数会发生什么,以及它为什么会起作用.:) 谢谢! (2认同)

ma1*_*w28 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 感谢您精彩的ASCII艺术解释:) (4认同)