微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

C - 反向递归链表:为什么使用双指针?

如何解决C - 反向递归链表:为什么使用双指针?

我理解 struct Node *head全局变量时的逻辑。

但是,当我在struct Node *head中使用main()作为局部变量进行相反的操作时,我必须使用双指针和Head的指针,我不明白在哪里正是我必须放置它们。

void Reverse(struct Node *Head,struct Node **headpointer) {
    struct Node *first;
    // when the list is empty
    if (Head == NULL)
        return;

    first = Head;
    // when there is one node left
    if (first->next == NULL) {
        *headpointer = first;
        return;
    }

    Reverse(first->next,headpointer);

    first->next->next = first;
    first->next = NULL;
}

我不清楚为什么我必须使用...

first = Head;
*headpointer = first;

为什么我不能使用

Head = first? 

另外,如果在递归函数调用 first = Head 前面有 Reverse(first->next,headpointer) 行,那么 first->next 的值不也等于 Head,它指向 {{1} }}节点?

有什么好的逻辑图/图片/解释/例子可以解释这种差异吗?

解决方法

我不清楚为什么我必须使用... first = Head

实际上,此代码中根本不需要 first 变量。它只是 Head 的副本,因此为了简化您可以将变量替换为 Head。没有它,一切都会一样:

if (Head == NULL)
  return;

if (Head->next == NULL) {
  *headpointer = Head;
  return;
}

Reverse(Head->next,headpointer);

Head->next->next = Head;
Head->next = NULL;

我不清楚为什么我必须使用... *headpointer = Head

Head 变量是指向要反转的链表头部的指针,函数将新反转的链表头部存储到 *headpointer 中。这样做是因为 Head 只是传递给 Reverse() 函数的指针的副本,因此更新其值不会更改原始指针;这就是使用单独的双指针变量的原因。

为什么我不能只使用 Head = first

Head 是传递给函数的指针的副本。更新 Head 指针不会更新您传入的原始列表指针。此外,正如我之前所说,Headfirst 相同,因此这样的赋值没有任何作用。

另外,如果在递归函数调用 first = Head 前面有 Reverse(first->next,headpointer) 行,那么 first->next 的值不也等于 Head,它指向 {{1} }}节点?

first(或first->next)只是列表中的下一个节点。函数调用的目的是先反转列表的其余部分,然后将Head->next(或Head)放在列表​​的末尾(这是最后两行所做的)。

,

让我们考虑一下函数签名是这样的:

void Reverse(struct Node* Head,struct Node* headpointer) {

你这样称呼它

Reverse(myList);

myList 只是一个节点的地址,例如 0x1234。所以它相当于做:

Reverse(0x1234);

地址被复制到一个新变量 headpointer。当我们修改 headpointer 时,我们正在修改一个 local 变量,而不是我们传递的 myList

就像我们这样做:

struct Node* myList = 0x1234;
struct Node* headpointer = myList;
headpointer = 0xABCD;
// at this point myList is still 0x1234

所以函数返回后,myList 仍然等于0x1234。这不是我们想要的,因为现在应该指向最后一个节点。

那么我们如何允许函数修改myList?我们必须告诉函数“嘿,这是您必须写入的地址”。

在 C 中,为了获取某事物的地址,我们使用了“&”运算符:

Reverse(&myList);

我们必须相应地更改函数的签名:

void Reverse(struct Node* Head,struct Node** headpointer) {

现在 headpointer 是一个地址到一个地址到一个 Node。或者,正如我们在 C 中所说的,一个指向 Node 的指针的指针。

这是一个有助于理解的最后一个例子。

struct Node* myList = 0x1234;
struct Node** headpointer = &myList; // now headpointer points to myList
*headpointer = 0xABCD;
// at this point myList is still 0xABCD
// we haven't changed headpointer,but the thing headpointer is pointing to!
,

必须能够返回更新的 head 节点,但这可以通过返回值来完成代替使用双指针。

另外,我相信函数需要被传递一个指向上一个节点的指针,以便它可以将它设置为new { {1}} 指针。

所以,我认为您的函数需要三个参数(例如)nextcurprev

如果我们返回 headpointer 指针,我们可以将其减少到两个


这是一个可以做到这一点的版本。

我已尝试尽可能多地对其进行注释。我添加了调试 head 和一些交叉检查 [我曾经自己调试]。

如果需要有一个双指针,这个例子可以很容易地适应。

printf

这是程序输出:

// revlist -- reverse singly linked list recursively

#include <stdio.h>
#include <stdlib.h>

typedef struct node {
    struct node *next;
    int data;
#ifdef CHECK
    int visited;                    // 1=node visited (check for looped list)
#endif
} Node;

#ifdef DEBUG
int dbglvl;                         // nesting level -- debug only
#endif

int
prt(Node *node)
{
    int data;

    if (node != NULL)
        data = node->data;
    else
        data = -1;

    return data;
}

#ifdef DEBUG
#define dbgprt(_fmt ...) \
    do { \
        printf("DBG/%d: ",dbglvl); \
        printf(_fmt); \
    } while (0)
#else
#define dbgprt(_fmt ...) \
    do { } while (0)
#endif

// reverse -- recursively reverse list
// RETURNS: pointer to head of reversed list
Node *
reverse(Node *cur,Node *prev)
// cur -- pointer to current node
// prev -- pointer to previous node
{
    Node *next;
    Node *head = NULL;

    do {
        // empty list
        if (cur == NULL)
            break;

        next = cur->next;

        dbgprt("BEG cur=%d prev=%d next=%d\n",prt(cur),prt(prev),prt(next));

        // at end of list -- set new head from tail node
        if (next == NULL) {
            head = cur;
            head->next = prev;
            dbgprt("SETHEAD head=%d head->next=%d\n",prt(head),prt(head->next));
            break;
        }

#ifdef DEBUG
        ++dbglvl;
#endif
        // process the next node and give the current node as the previous one
        head = reverse(next,cur);
#ifdef DEBUG
        --dbglvl;
#endif

        // set the link pointer to the previous node
        cur->next = prev;
        dbgprt("POST cur->next=%d\n",prt(cur->next));
    } while (0);

    dbgprt("EXIT head=%d\n",prt(head));

    return head;
}

// addnode -- add node to tail of list
Node *
addnode(Node *head,int data)
{
    Node *newnode = malloc(sizeof(*newnode));
    Node *cur;
    Node *prev;

    newnode->next = NULL;
    newnode->data = data;
#ifdef CHECK
    newnode->visited = 0;
#endif

    // find the tail of the list
    prev = NULL;
    for (cur = head;  cur != NULL;  cur = cur->next)
        prev = cur;

    // add new node to list
    if (prev != NULL)
        prev->next = newnode;
    else
        head = newnode;

    return head;
}

// list_print -- print list
void
list_print(Node *head,const char *reason)
{
    Node *cur;

    printf("%s:",reason);

    for (cur = head;  cur != NULL;  cur = cur->next) {
        printf(" %d",cur->data);
#ifdef CHECK
        if (cur->visited) {
            printf(" DUP\n");
            exit(1);
        }
        cur->visited = 1;
#endif
    }

    printf("\n");

#ifdef CHECK
    for (cur = head;  cur != NULL;  cur = cur->next)
        cur->visited = 0;
#endif
}

// list_destroy -- destroy the list
void
list_destroy(Node *cur)
{
    Node *next;

    for (;  cur != NULL;  cur = cur->next) {
        next = cur->next;
        free(cur);
    }
}

// dotest -- test reverse function
void
dotest(int max)
{
    Node *head = NULL;

#ifdef DEBUG
    dbglvl = 0;
#endif

    // create a list
    for (int idx = 1;  idx <= max;  ++idx)
        head = addnode(head,idx);

    // show the original list
    list_print(head,"Forward");

    // reverse the list
    head = reverse(head,NULL);

    // show the reversed list
    list_print(head,"Reverse");

    // destroy the test list
    list_destroy(head);
}

int
main(void)
{

    dotest(8);

    return 0;
}

这是启用调试 Forward: 1 2 3 4 5 6 7 8 Reverse: 8 7 6 5 4 3 2 1 的程序输出:

printf

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。