链表OJ题目详解(1)--- 移除链表元素

文章详细介绍了三种不同的方法来删除链表中具有特定值的元素:一是遍历链表,遇到匹配值时释放节点并调整连接;二是使用尾插法构建新链表;三是引入哨兵节点简化头节点处理。每种方法都针对特殊情况如头节点删除和空链表进行了处理,并提供了正确的C语言实现代码。
摘要由CSDN通过智能技术生成

1.移除链表元素

 思路一:遍历整个链表

①给定val等于当前节点的val,就释放节点,把该节点前一个节点和该节点后一个节点链接

②给定val不等于当前节点的val,就继续往后走

由于释放节点后我们需要链接前一个节点和后一个节点,因此需要定义一个指针变量prev,记录当前节点的上一个节点

情况一:给定的val不等于当前节点val

 情况二:给定的val等于当前节点val

①让prev指向该节点的下一个节点        prev->next = cur->next

 ②释放当前节点        free(cur)

③cur往后走        cur = prev->next

 最后返回头节点

但是提交时会发现一个问题就是第一个节点的val就是要删除的数据时(head == cur或者prev == NULL),prev->next会产生空指针解引用行为而导致错误,因此我们需要特殊处理一下

 这时只需要释放头节点让cur向后走,并且更新头节点

正确代码

struct ListNode* removeElements(struct ListNode* head, int val) {
	struct ListNode* cur = head;
	struct ListNode* prev = NULL;
	while (cur)
	{
		if (cur->val == val)
		{
			if (head == cur)
			{
				cur = cur->next;
				free(head);
				head = cur;
			}
			else
			{
				prev->next = cur->next;
				free(cur);
				cur = prev->next;
			}
		}
		else
		{
			prev = cur;
			cur = cur->next;
		}
	}
	return head;
}

思路二:尾插

建立一个新的链表,释放val值等于给定val的节点,不等于给定val值的节点依次尾插到新的链表,返回新链表的头

 初始情况: 新链表的头和尾指针都赋值成空指针

①第一个节点val值不等于6,则尾插,由于newhead和cur开始为空,因此这次尾插时 newhead = tail = cur,然后cur = cur->next, cur指向了第二个节点

 ②第二个节点val不等于6,尾插,这时只需要tail->next = cur,便实现了尾插,同时让tail = cur,使得tail时刻指向新链表的尾节点

 ③第三个节点val等于6,释放该节点,并让cur指向下一个节点,当然要先设置一个变量del 保存下一个节点地址,然后释放该节点, 再让cur =del

后面的操作思路也是一样的,就不一一介绍了

但是如果这样直接提交,会发现不能通过,会报内存错误,而内存错误一般都是野指针这种错误n那么为什么会出现野指针的问题呢?

我们来看当最后一个节点是要删除的节点会发生什么?

 5不是要删除的节点,尾插到新链表中,但是原本5这个节点的next保存了下一个节点的地址,也就是最后一个节点的地址,因此将6节点释放后,tail就会成为野指针,因为我们最后要将tail->next置空

但是又出现了新的问题,根据题中给的示例以及提示的错误信息,我们发现是当传入链表为空时,while循环没进去,直接来到tail->next = NULL这条语句,tail就是空指针,因此产生了空指针解引用问题

所以可以在开始加上判断头为空直接返回空的条件判断,我们也可以在最后给tail判空的时候进行条件判断

正确代码

struct ListNode {
    int val;
    struct ListNode *next;
};
struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode* newhead = NULL;
    struct ListNode* tail = NULL;
    struct ListNode* cur = head;
    while(cur)
    {
        if(cur->val == val)
        {
            struct ListNode* del = cur->next;
            free(cur);
            cur = del;
        }
        else
        {
            if(newhead == NULL)
            {
                newhead = tail = cur;
                cur = cur->next;
            }
            else
            {
                tail->next = cur;
                tail = cur;
                cur = cur->next;
            }
        }
    }
    if(tail)
    tail->next = NULL;
    return newhead;
}

思路三:带哨兵位头节点

       带哨兵位头节点的链表这种结构往往能给我们带来很多的优势,我们看到了在思路一中,我们必须考虑到当要删除元素的节点是头节点时的处理办法,确实有些麻烦,我们可以强行在题中所给链表加一个带哨兵位头节点(malloc创建并链接链表),这时要注意prev的初值要改为guard

 最后记得释放创建的guard并置空(leetcode不会检查,但这是一种良好的习惯哦)

正确代码

struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));
    guard->next = head;
    struct ListNode* cur = head;
	struct ListNode* prev = guard;
	while (cur)
	{
		if (cur->val == val)
		{
            prev->next = cur->next;
			free(cur);
			cur = prev->next;
		}
		else
		{
			prev = cur;
			cur = cur->next;
		}
	}
    head = guard->next;
    free(guard);
    guard = NULL;
	return head;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值