补充:OJ题4: 移除链表元素

嗨!小伙伴们,大家还记得这道题吗?

刚才我们讲解了方法一,接下来我们来讲讲方法二。

法二: 我们可以用一个pcur变量依次遍历原链表的每一个结点,如果pcur->val等于要删除的数据val,那么执行链表的删除操作。

首先,我们定义一个哨兵结点(只是指向头结点,里面没有任何数据,便于执行删除操作),将哨兵结点的next指针指向头结点; 定义一个pcur变量,依次遍历链表的每一个结点。定义一个prev指针,用来保存被删除结点的前一个结点。

当pcur指向第一个结点时,进入循环,开始判断链表中每一个结点的数据域是否为val,如果等于val,那么执行删除操作; 如果不等于val,prev指针保存pcur的位置,pcur指向下一个结点,继续往后判断。

第一次:(我们假设val的值为6)pcur指向的结点的数据域不是val(pcur->val != val), 那么就把pcur的值赋给prev,pcur指向下一个结点。

第二次:pcur指向的结点的数据域不是val(pcur->val != val), 那么就把pcur的值赋给prev,pcur指向下一个结点。

第三次:pcur指向的结点的数据域为val(pcur->val == val), 那么就把pcur用一个临时变量del保存起来,prev指向pcur的下一个结点,同时释放del,此时pcur为一个野指针,那么就把pcur指向prev结点的next指针。

prev->next = pcur->next;        //prev指针指向pcur的下一个结点
struct ListNode* del = pcur;    //用临时变量del来保存pcur结点
free(del);                      //将del结点释放,pcur现在是野指针
pcur = prev->next;              //pcur指向prev结点的next指针

亦或者,我们也可以这样写:

prev->next = pcur->next;          //prev结点的next指针指向pcur的下一个结点
struct ListNode* del = pcur;      //用临时变量del保存pcur结点
pcur = pcur->next;                //pcur结点指向下一个结点
free(del);                        //释放del结点,对pcur没有造成影响

第四次: pcur指向的结点的数据域不是val(pcur->val != val), 那么就把pcur的值赋给prev,pcur指向下一个结点。

第五次: pcur指向的结点的数据域不是val(pcur->val != val), 那么就把pcur的值赋给prev,pcur指向下一个结点。

第六次:  pcur指向的结点的数据域不是val(pcur->val != val), 那么就把pcur的值赋给prev,pcur指向下一个结点。

第七次: pcur指向的结点是val,执行删除操作。

最后我们找到第一个结点,释放哨兵结点,本题完成。

本题代码如下:

 typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {
        //带哨兵位

        //定义一个哨兵节点,不用考虑头删操作
        ListNode* node =(ListNode*) malloc(sizeof(ListNode));
        //将第一个节点head赋给哨兵节点的next指针
        node->next = head;
        //prev指针指向哨兵节点
        ListNode* prev = node;
        //pcur指针指向第一个节点head
        ListNode* pcur = head;

        //当pcur遍历到NULL时,退出循环
        while(pcur!=NULL){
        //如果当前节点的val值和整数val值相等,那么就删除当前节点
            if(pcur->val == val){
                prev->next = pcur->next;//更新prev指针
                ListNode* del = pcur;
                pcur = pcur->next;      //更新pcur指针
                free(del);              //释放当前节点
            }else{
        //如果当前节点的val值和整数val值不相等,
        //那么prev保存当前pcur指针指向的节点
        //pcur指针指向下一个节点
                prev = pcur;           
                pcur = pcur->next;
            }
        }

        //(因为第一个节点有可能被删除,所以不能是 node->next = head)
        //这里,哨兵节点的next指针指向的节点就是第一个节点head
        //head = node->next;
        head = node->next;
        free(node);         //释放哨兵节点
        return head;        //返回第一个节点head
}

       当然啦,我们也可以选择不带哨兵位,但是必须要考虑头删的情况,代码如下:

 typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {
            //不带哨兵位

            //prev指针指向空(NULL)
            ListNode* prev = NULL;
            //pcur指针指向第一个节点head
            ListNode* pcur = head;

            //当pcur遍历到NULL时,退出循环
            while(pcur!=NULL){
            //如果当前节点的val值为val
                if(pcur->val == val){
                    //判断头删情况
                    //如果此时恰好第一个节点被删除,也就是出现了头删情况
                    if(pcur == head){
                        head = pcur->next;        //pcur的next指针赋给头结点
                        free(pcur);               //释放当前节点
                        pcur = head;              //更新pcur节点
                    }else{
                        prev->next = pcur->next;  //更新prev节点的next指针
                        ListNode* del = pcur;
                        pcur = pcur->next;        //更新pcur节点
                        free(del);                //释放当前节点
                    }
                }else{
                //如果当前节点的val值不是val
                        prev = pcur;               //prev指针保留pcur指针原来的位置
                        pcur = pcur->next;         //pcur指针指向下一个节点
                }
            }

            return head;                            //返回第一个节点head
}

注意啦,带哨兵节点和不带哨兵节点的区别是啥?

区别:
①有哨兵位的话,需要单独开辟一个空间,存放哨兵位,哨兵位的next指针指向第一个结点(node->next = head);
②有哨兵位的话,prev指针指向哨兵位,如果没有,那么prev指针指向NULL
③如果没有有哨兵位,则需要判断头删的情况,改变head的位置,同时更新cur,指向新的结点;如果有哨兵位,则无需判断头删的情况
④如果有哨兵位,最后需要释放哨兵位,哨兵位的next指针指向的就是第一个结点;木有哨兵位,则直接返回第一个结点。

片尾

今天我们学习了移除链表元素的2种方法,希望看完这篇文章能对友友们有所帮助!!!

点赞收藏加关注!!!

谢谢大家!!!

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值