C语言编程--16.删除链表的倒数第n个节点
题目:
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
提示:
链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
代码:
/*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/
// 函数用于从单链表的末尾删除第 n 个节点,并返回新的链表头指针
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {// 用于遍历链表的指针,初始指向链表头struct ListNode* node = head;// 用于记录链表节点的数量int count = 0; // 遍历链表,统计节点数量while (node) {node = node->next;count++;}// 如果要删除的节点是头节点(即链表长度等于 n)if (count == n) {// 保存原头节点的下一个节点struct ListNode* temp = head->next;// 将头指针指向下一个节点,相当于删除了原头节点head = head->next;// 返回新的头节点return head;}// 将指针重新指向链表头,准备再次遍历node = head;// 计算要删除节点的前一个节点的位置int m = count - n;// 遍历到要删除节点的前一个节点for (int i = 0; i < m - 1; i++)node = node->next;// 保存要删除的节点struct ListNode* temp = node->next;// 将前一个节点的 next 指针指向要删除节点的下一个节点,跳过要删除的节点node->next = node->next->next; // 返回原链表的头节点(链表已被修改)return head;
}
代码分析:
优点:
- 逻辑清晰:代码采用了先遍历一次链表统计节点数量,再根据数量计算要删除节点的位置,最后进行删除操作的思路,整体逻辑比较直观,容易理解。
- 处理头节点情况:通过判断链表长度和 n 的关系,专门处理了要删除的节点是头节点的情况,保证了代码的正确性。
缺点:
- 两次遍历链表:代码中先对链表进行了一次遍历统计节点数量,然后又进行了一次遍历找到要删除节点的前一个节点。这种方式增加了时间复杂度,使得时间复杂度为 O(n),在一些对时间要求较高的场景下可能效率较低。
- 未释放内存:在删除节点时,保存了要删除的节点指针 temp,但没有释放该节点占用的内存,会导致内存泄漏问题。
- 边界条件处理不够完善:代码仅处理了要删除的节点是头节点的情况,对于其他一些特殊情况(例如空链表等)没有进行额外的处理,健壮性不足。
- 没有使用虚拟头节点:使用虚拟头节点可以简化链表操作,特别是在处理头节点删除等情况时,可以避免一些特殊情况的判断,使代码更加简洁和统一。但此代码没有采用这种方式。
为了改进这些缺点,可以考虑使用双指针法(快慢指针),只遍历一次链表就能找到要删除的节点,同时注意释放内存和完善边界条件的处理。