引言
链表是面试和算法竞赛中的高频考点。相比数组,链表在插入和删除操作上具有 O(1) 的优势,但随机访问能力较弱。掌握链表的核心在于对指针操作的精准把控以及对边界条件的处理。下面通过 10 道经典题目,结合 C 语言实现,逐步拆解其中的常用技巧,如哨兵节点、快慢指针、双指针等。
1. 删除链表中等于给定值 val 的所有结点
题目描述
删除链表中所有值为 val 的结点,返回删除后的链表头结点。
参考:力扣 203. 移除链表元素
思路分析
直接操作原链表头结点容易丢失引用,尤其是当需要删除头结点时。这里推荐使用哨兵节点(哑结点)。创建一个新节点作为虚拟头,遍历原链表,将不等于 val 的节点依次接到新链表后面。最后记得将尾节点的 next 置为 NULL,并释放哨兵节点内存。
代码实现
typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {
if (head == NULL) return NULL;
// 创建哨兵节点
ListNode* newhead = (ListNode*)malloc(sizeof(ListNode));
ListNode* newtail = newhead;
ListNode* pcur = head;
while (pcur) {
if (pcur->val != val) {
newtail->next = pcur;
newtail = newtail->next;
}
pcur = pcur->next;
}
newtail->next = NULL; // 关键:断开尾部连接
ListNode* ret = newhead->next;
free(newhead);
return ret;
}
2. 反转一个单链表
题目描述 反转一个单链表,返回反转后的链表头结点。 参考:力扣 206. 反转链表
思路分析
迭代法是最直观的解法。我们需要三个指针:prev(前驱)、curr(当前)、next(后继)。初始时 prev 为 NULL, 指向头结点。每次循环中,先保存 ,然后将 指向 ,最后三个指针整体后移。当 为 时, 即为新的头结点。


