[英]C - linked list reverse recursively: why use double pointer?
我理解struct Node *head
是全局變量時的邏輯。
但是,當我在main()
中使用struct Node *head
作為局部變量進行反向操作時,我必須使用雙指針和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
line 在遞歸 function call Reverse(first->next, headpointer)
, first->next
值不也等於指向第first
節點的Head
嗎?
是否有任何好的邏輯圖/圖片/解釋/示例可以解釋這種差異?
我不清楚為什么我必須使用...
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
變量是指向要反轉的列表頭部的指針,function 將新反轉的列表的頭部存儲到*headpointer
中。 這樣做是因為Head
只是傳遞給Reverse()
function 的指針的副本,因此更新其值不會更改原始指針; 這就是使用單獨的雙指針變量的原因。
為什么我不能只使用
Head = first
?
Head
是傳遞給 function 的指針的副本。 更新Head
指針不會更新您傳入的原始列表指針。另外,正如我之前所說, Head
與first
相同,因此這樣的賦值沒有任何作用。
另外,如果
first = Head
line 在遞歸 function callReverse(first->next, headpointer)
,first->next
值不也等於指向第first
節點的Head
嗎?
first->next
(或Head->next
)只是列表中的下一個節點。 function 調用的目的是先反轉列表的 rest,然后將Head
(或first
)放在列表的末尾(這是最后兩行所做的)。
讓我們考慮 function 簽名是這樣的:
void Reverse(struct Node* Head, struct Node* headpointer) {
你這樣稱呼它
Reverse(myList);
myList
只是一個節點的地址,例如0x1234
。 所以它相當於做:
Reverse(0x1234);
地址被復制到一個新的變量headpointer
。 當我們修改headpointer
時,我們正在修改一個局部變量,而不是我們傳遞的myList
。
就像我們這樣做:
struct Node* myList = 0x1234;
struct Node* headpointer = myList;
headpointer = 0xABCD;
// at this point myList is still 0x1234
所以 function 返回后, myList
仍然等於0x1234
。 這不是我們想要的,因為 is 現在應該指向最后一個節點。
那么我們如何允許 function 修改myList
呢? 我們必須告訴 function “嘿,這是你必須寫信的地址”。
在 C 中,為了獲取我們使用“&”運算符的地址:
Reverse(&myList);
我們必須相應地更改 function 的簽名:
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
節點,但這可以通過返回值代替使用雙指針來完成。
另外,我相信 function需要傳遞一個指向前一個節點的指針,以便它可以將其設置為新的next
指針。
所以,我認為你的 function 需要三個arguments (例如) 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;
}
這是程序 output:
Forward: 1 2 3 4 5 6 7 8
Reverse: 8 7 6 5 4 3 2 1
這是啟用了調試printf
的程序 output:
Forward: 1 2 3 4 5 6 7 8
DBG/0: BEG cur=1 prev=-1 next=2
DBG/1: BEG cur=2 prev=1 next=3
DBG/2: BEG cur=3 prev=2 next=4
DBG/3: BEG cur=4 prev=3 next=5
DBG/4: BEG cur=5 prev=4 next=6
DBG/5: BEG cur=6 prev=5 next=7
DBG/6: BEG cur=7 prev=6 next=8
DBG/7: BEG cur=8 prev=7 next=-1
DBG/7: SETHEAD head=8 head->next=7
DBG/7: EXIT head=8
DBG/6: POST cur->next=6
DBG/6: EXIT head=8
DBG/5: POST cur->next=5
DBG/5: EXIT head=8
DBG/4: POST cur->next=4
DBG/4: EXIT head=8
DBG/3: POST cur->next=3
DBG/3: EXIT head=8
DBG/2: POST cur->next=2
DBG/2: EXIT head=8
DBG/1: POST cur->next=1
DBG/1: EXIT head=8
DBG/0: POST cur->next=-1
DBG/0: EXIT head=8
Reverse: 8 7 6 5 4 3 2 1
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.