每日一题(移除链表元素)

每日一题(移除链表元素)

203. 移除链表元素 - 力扣(LeetCode)

每日一题(移除链表元素)_第1张图片

思路一:

可以创建一个新的链表头节点newhead,只要是原链表中值不为val的节点、都通过尾插操作插到newhead所指向的链表中,原链表中值为val的节点直接删除释放掉。为了让尾插操作更方便,还需要再定义一个tail指针,用于记录newhead链表中的最后一个元素的地址。(所以每次尾插之后tail指针要进行更新)newhead保持不变,永远指向第一个节点。所以共要,创建四个结构体指针:cur、newhead、next、tail。cur指针用于遍历原链表,newhead用于记录新链表的头,最后函数的返回值就用newhead进行返回,next指针用于记录cur指针的下一个位置,方便进行尾插操作,tail指针用于记录newhead链表中的最后一个元素,并且每一次的尾插之后都要进行更新。

思路二:

直接使用cur指针和prev指针遍历原链表,cur指针从原链表的头开始遍历,prev用于记录cur的上一个节点的地址。假如cur指向的节点的值为val,先让prev和cur节点的next连接起来。接着释放cur指向的节点。(但是假如cur一开始指向head时,该节点的值也恰好为val,则此时要进行头删操作,并且释放之后的节点无效了,需要更新head。)

思路一代码:

struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode* cur = head;
    struct ListNode* next = head;
    struct ListNode* newhead = NULL;
    struct ListNode* tail = newhead;
    while(cur)
    {
        //满足删除条件
        if(cur ->val != val)
        {
            //假如newnode是空
            if(newhead== NULL)
            {
                next = cur->next;
                newhead = cur;
                tail = newhead;
                cur = next;
            }
            //newnode不是空
            else
            {
                next = cur->next;
                tail->next = cur;
                tail = tail->next;
                cur = next;
            }
        }
        //不满足删除条件
        else
        {
            next = cur->next;
            free(cur);
            cur = next;
        }
    }
        if(tail!=NULL)
        tail->next = NULL;
        return newhead;
}

思路二代码:

    struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode* prev=NULL;
    struct ListNode* cur = head;
    while(cur)
    {
        //节点的值满足删除条件
        if(cur->val == val)
        {
            //当删除的是头节点
            if(cur == head)
            {
                head = cur->next;
                free(cur);
                cur = head;
            }
            //当删除的不是头节点
            else
            {
                prev->next = cur->next;
                free(cur);
                cur = prev->next;
            }
        }

        //节点的值不满足删除条件
        else//向后走
        {
            prev = cur;
            cur=cur->next;
        }
    }
    return head;
}

注意:以下都是针对思路一的讲解:

图解

在代码的初始状态是如下图所示的:此时的next和cur都指向head,也就是原链表中的第一个元素。newhead和tail为空指针时,要进行特判,记录cur的下一个位置之后,要先让newhead指向cur,同时tail指针也向后移动了一步指向cur,完成了更新。

每日一题(移除链表元素)_第2张图片

当来到下图所示的常规情况时,此时的tail和newhead都不为空,newhead不能动,记录cur的next之后,直接改变tail的next并且更新tail即可。

每日一题(移除链表元素)_第3张图片

当代码进行到如下图所示时,此时cur为空指针,while循环结束,按常理来讲就应该要返回newhead了。(单链表的最后一个节点的next指针的值必须是NULL)但是,此时的的tail的next指针仍然是指向6所在的节点的,并且该节点虽然被释放了,但是值就会变成随机值,并不一定是NULL,所以在返回newhead之前,要将tail的next置为空指针。

每日一题(移除链表元素)_第4张图片

注意:当测试用例使用空链表测试时,cur的值也为NULL,while循环并没有进入。也就说明tail的值一直都是NULL并没有被改变,如果此时仍然使用tail->next = NULL;强行置空就会出现空指针访问错误。所以此处需要进行特判。

完结

本题的全部分析就到这里啦,若有不足,欢迎评论区指正,下期见!

你可能感兴趣的:(数据结构与算法,链表,数据结构,c语言)