嗨!小伙伴们,大家还记得这道题吗?
刚才我们讲解了方法一,接下来我们来讲讲方法二。
法二: 我们可以用一个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种方法,希望看完这篇文章能对友友们有所帮助!!!
求点赞收藏加关注!!!
谢谢大家!!!