指向后一个节点
在 C 语言中,dummy.next = head;
和 dummy->next = head;
这两种写法有着不同的含义,具体使用哪种形式取决于 dummy
的定义方式。下面是对这两种写法的详细解释:
1. dummy.next = head;
这种写法通常用于当 dummy
是一个结构体类型的变量时。例如,如果你定义了一个结构体 ListNode
,那么你可能会这样定义 dummy
:
struct ListNode dummy; dummy.next = head; // 这是有效的
在这种情况下,dummy
是一个 ListNode
结构体的实例。你可以直接通过点操作符(.
)访问其成员。next
是 ListNode
结构体中的一个指针成员,指向链表中的下一个节点。
2. dummy->next = head;
这种写法通常用于当 dummy
是一个指向结构体的指针时。例如:
struct ListNode *dummy = malloc(sizeof(struct ListNode)); // 分配内存给 dummy
dummy->next = head; // 使用箭头操作符来访问成员
在这种情况下,dummy
是一个指向 ListNode
结构体的指针,因此你需要使用箭头操作符(->
)来访问其成员 next
。
代码示例
下面是一个完整的示例,展示了这两种写法的使用:
#include <stdio.h>
#include <stdlib.h>
struct ListNode {
int val;
struct ListNode *next;
};
struct ListNode* createNode(int value) {
struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
newNode->val = value;
newNode->next = NULL;
return newNode;
}
void printList(struct ListNode* head) {
struct ListNode* current = head;
while (current != NULL) {
printf("%d -> ", current->val);
current = current->next;
}
printf("NULL\n");
}
struct ListNode* swapPairs(struct ListNode* head) {
struct ListNode dummy; // dummy 是一个结构体变量
dummy.next = head; // 使用 . 访问成员
struct ListNode *prev = &dummy; // prev 指向 dummy
while (prev->next != NULL && prev->next->next != NULL) {
struct ListNode *first = prev->next;
struct ListNode *second = first->next;
// 交换节点
first->next = second->next;
second->next = first;
prev->next = second; // 更新 prev 的指向
prev = first; // 移动到下一个待交换的节点
}
return dummy.next; // 返回新的头节点
}
int main() {
// 创建链表 1 -> 2 -> 3 -> 4
struct ListNode* head = createNode(1);
head->next = createNode(2);
head->next->next = createNode(3);
head->next->next->next = createNode(4);
printf("Original list: ");
printList(head);
head = swapPairs(head); // 交换节点
printf("Swapped list: ");
printList(head);
return 0;
}
指向前一个节点
在链表中,通常我们使用一个指针来表示当前节点的前一个节点。由于单向链表的结构特性(每个节点只有指向下一个节点的指针),我们无法直接从当前节点访问前一个节点。因此,我们通常需要在遍历链表时维护一个额外的指针来追踪前一个节点。下面是一些常见的方法来表示和处理前一个节点:
1. 使用额外的指针
在遍历链表的过程中,使用一个指针 prev
来记录当前节点的前一个节点。例如:
struct ListNode* swapPairs(struct ListNode* head) {
// 创建一个虚拟头节点
struct ListNode dummy;
dummy.next = head;
struct ListNode *prev = &dummy; // prev 指向虚拟头节点
while (prev->next != NULL && prev->next->next != NULL) {
struct ListNode *first = prev->next;
struct ListNode *second = first->next;
// 交换节点
first->next = second->next;
second->next = first;
prev->next = second; // 更新前一个节点的指向
prev = first; // 移动 prev 到刚刚交换后的第一个节点
}
return dummy.next; // 返回新的头节点
}
2. 使用双指针技术
双指针技术是一种常用的技巧,通常用来解决链表的各种问题。在这种方法中,prev
和 current
指针会同时向前移动,从而实现对当前节点及其前一个节点的跟踪。
void printList(struct ListNode* head) {
struct ListNode *current = head;
struct ListNode *prev = NULL; // 初始化前一个节点为 NULL
while (current != NULL) {
printf("当前节点: %d ", current->val);
if (prev != NULL) {
printf("前一个节点: %d\n", prev->val);
} else {
printf("前一个节点: NULL\n");
}
prev = current; // 更新前一个节点
current = current->next; // 移动到下一个节点
}
}
3. 使用递归(对于某些场景)
在某些情况下,可以使用递归来解决链表问题。在递归函数的参数中,可以传递前一个节点的指针。例如:
void reverseList(struct ListNode* current, struct ListNode* prev, struct ListNode** newHead) {
if (current == NULL) {
*newHead = prev; // 到达链表尾部,更新新的头节点
return;
}
struct ListNode* nextNode = current->next; // 保存下一个节点
current->next = prev; // 反转指针
reverseList(nextNode, current, newHead); // 递归处理
}
struct ListNode* reverse(struct ListNode* head) {
struct ListNode* newHead = NULL;
reverseList(head, NULL, &newHead); // 调用递归函数
return newHead; // 返回反转后的新头节点
}
总结
- 在遍历或操作链表时,通常使用一个指针(如
prev
)来表示当前节点的前一个节点。 - 在许多情况下,你会同时维护两个指针,一个指向当前节点,一个指向前一个节点。
- 递归方法也可以用于某些链表问题,通过传递前一个节点作为参数。
这种方法有效地解决了在单向链表中无法直接访问前一个节点的问题。如果你还有其他问题,或需要更具体的例子,请随时告诉我!