题目描述:给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
方法一:两重遍历(链表的遍历和删除)
(1)链表的遍历:利用临时节点一次遍历链表中的每一个节点,判断节点中的指针域是否为NULL,否则继续向后遍历;向后遍历过程为指向当前节点的指针,赋值为指向下一个节点的地址,即当前节点中的指针域;
(2)链表(a->b->c)删除节点b:删除节点b,只需要使节点a的指针域指向节点c即可,则该链表中不存在节点b;b的内存如何释放?节点内部动态申请的内存需要手动释放,简单数据结构由系统自动处理;
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeNthFromEnd(struct ListNode* head, int n)
{
//二重遍历
int lenAll = 0;
struct ListNode *nodeTemp = head;
//求链表长度
while (nodeTemp != NULL) {
lenAll++;
nodeTemp = nodeTemp->next;
}
//异常情况
if (lenAll == n) {
return head->next;
}
//删除链表指定位置
nodeTemp = head;
//链表遍历到指定位置:被删除节点的前一个节点
for (int i = 0; i < (lenAll - n -1); i++) {
nodeTemp = nodeTemp->next;
}
//删除节点:复杂结构需要手动提前释放内存
nodeTemp->next = nodeTemp->next->next;
return head;
}
方法二:单次遍历(双指针法,滑动窗口)
(1)保证low和high的距离固定为n,则当high指向尾节点时,low就为倒数第n个节点
(2)删除节点的操作,需要指向被删除节点的上一个节点
struct ListNode* removeNthFromEnd(struct ListNode* head, int n)
{
struct ListNode *low = head;
struct ListNode *high = head;
//将high前移n个节点 (删除节点时,需要保证low指在被删除的前一个节点)
for (int i = 0; i < n - 1; i++) {
high = high->next;
}
//high已经为尾节点,则被删除的肯定为第一个节点
if (high->next == NULL) {
return head->next;
}
//同时移动low,high,直到high为倒数第二个节点,因为只有这样,low才会指向被删除节点的前一个节点
while (high->next->next != NULL) {
low = low->next;
high = high->next;
}
//删除low后面的一个节点
low->next = low->next->next;
return head;
}