修改单链表时传入二级指针详解
我们先来看一个例子:
1.通过调用函数改变a的值:
int *p = &a;
notice: p的值,*p,&p注意区分
p的值:就是变量a的地址:0x11
*p: 就是变量a的值:1
&p:就是p的地址:ox22
p代表的是a的地址,即:&a。如果要修改a的值,要传入p或者&a,即:func(p )或者func(&a)。
p 是一个指向 int 类型的指针(int*),&a也是一个指向 int 类型的指针(int*)
func(int * pa) 需要一个指向整数的指针 (int*)。函数 func 接受一个指向整数的指针 pa,并将 pa指向的值修改为2。
2.1.通过调用函数改变p的值:
p的值:就是变量a的地址:0x11
&p:就是p的地址:ox22
调用函数void fun(int **pa),将指针的地址&p传入,此时pa=&p;pa的值也就是p的地址(&p):0x22,而*pa就是p的值:ox11,我们需要修改p的值,也就是p的指向。
&p 的含义是取指针变量 p 的地址。因为 p 是一个指向 int 类型的指针(int*),所以 &p 的类型是 int**,即指向 int 指针的指针。
函数 func 的执行
当 func(&p) 被调用时,以下操作发生:
pa在函数 func 中被初始化为 p 的地址。
*pa解引用 pa,即 *pa 现在是指向 a 的指针 p。
*pa= &c 将 p 修改为指向 c 的地址。
所以在 func(&p) 调用之后,p 不再指向 a,而是指向 c。
如果理解了上面的例子那么就知道了为什么修改单链表时要传入二级指针
一、使用 struct Node** 情况
当修改原始链表的头指针时,需要使用二重指针 struct Node** head_ref。这种情况通常发生在以下场景:
1.在链表头部插入节点:因为插入新的头节点后,原来的头指针需要指向新的头节点。
2.删除链表中的某个节点:如果删除的是头节点,你需要修改头指针。
示例:在链表头部插入节点
void insertAtHead(struct Node** head_ref, int new_data) {
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
new_node->data = new_data;
new_node->next = (*head_ref);
(*head_ref) = new_node;
}
示例:删除链表中的某个节点
void deleteNode(struct Node** head_ref, int key) {
struct Node* temp = *head_ref, *prev;
if (temp != NULL && temp->data == key) {
*head_ref = temp->next; // Changed head
free(temp); // free old head
return;
}
while (temp != NULL && temp->data != key) {
prev = temp;
temp = temp->next;
}
if (temp == NULL) return;
prev->next = temp->next;
free(temp);
}
二、使用 struct Node* 情况
当不需要修改原始链表的头指针,只是遍历链表或修改链表中其他节点时,可以使用单指针 struct Node* head_ref。这种情况通常发生在以下场景:
1.在链表中间或尾部插入节点:你只需要修改特定节点的 next 指针,不需要修改头指针。
2.遍历链表:只需要读取头指针的值,而不需要修改它。
示例:在链表中间插入节点
void insertAfter(struct Node* prev_node, int new_data) {
if (prev_node == NULL) {
printf("The given previous node cannot be NULL");
return;
}
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
new_node->data = new_data;
new_node->next = prev_node->next;
prev_node->next = new_node;
}
示例:在链表尾部插入节点
void insertAtEnd(struct Node* head_ref, int new_data) {
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
struct Node* last = head_ref;
new_node->data = new_data;
new_node->next = NULL;
if (head_ref == NULL) {
head_ref = new_node;
return;
}
while (last->next != NULL) {
last = last->next;
}
last->next = new_node;
}
注意,如果链表为空,在尾部插入节点时,单指针版本可能会出现问题,因为它不能修改原始的头指针。可以改为二重指针以处理这种情况。
改进后的在链表尾部插入节点的函数:
void insertAtEnd(struct Node** head_ref, int new_data) {
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
struct Node* last = *head_ref;
new_node->data = new_data;
new_node->next = NULL;
if (*head_ref == NULL) {
*head_ref = new_node;
return;
}
while (last->next != NULL) {
last = last->next;
}
last->next = new_node;
}
改变链表的头指针就传二级指针,即:指向指针的指针。改变头指针不能传一级指针因为传送的过程就是拷贝的过程,相当于将头指针复制了一份,形参的改变不会影响实参,因此要改变链表的头指针需要传送二级指针。
总结:
使用 struct Node**:当需要修改原始链表的头指针时。
使用 struct Node*:当不需要修改原始链表的头指针,仅进行遍历或修改其他节点时。