问题描述
我理解 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
指针不会更新您传入的原始列表指针。此外,正如我之前所说,Head
与 first
相同,因此这样的赋值没有任何作用。
另外,如果在递归函数调用 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}} 指针。
所以,我认为您的函数需要三个参数(例如)next
、cur
、prev
。
如果我们返回 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