链表中的经典问题——移除链表元素I

移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

 题解一,迭代法:

解题思路:

        1. 首先需要考虑,链表为空或head节点的值等于val的情况,这里可以引入一个虚拟的哑节点dummy_node,该哑节点指向头节点,引入哑节点的好处就是不用特殊考虑链表为空或头节点为空的情况。

        2. 定义一个current变量,指向哑节点dummy_node;

        3. 从current.next开始遍历,直到为None;

        4. 遍历过程中,分两种情况处理,当current.next.val==val时,那么需要删掉该节点,指向下一个节点;

        5. 当current.next.val!=val时,继续执行遍历

代码实现:

class Solution:
    def remove_elements(self,head:Optional(ListNode), val:int)-> Optional(ListNode):
        
        # 定义一个虚拟哑节点
        dummy_node = ListNode(0)
        
        # 将哑节点指向头节点
        dummy_node.next = head

        # 定义一个current变量指向哑节点
        current = dummy_node
           
        # 从current.next开始遍历
        while current.next:
            
            if current.next.val == val:
                # 这里current.next指向了新的节点,那dummy_node也重新指向新的头节点
                current.next = current.next.next
            else:
                # 继续遍历
                current = current.next
        
        return dummy_node.next

# 为什么没有返回head,而是返回的dummy_node.next,因为当head.val==val时,并没有删除头节点即head,但是current.next = current.next.next却更新了dummy_node.next,即新的头节点

题解二,递归法:

解题思路:

代码实现:

class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        if not head:
            return head 

        # 这里使用了递归调用 self.removeElements(head.next, val),它的作用是删除链表中除头节点外值为 val 的节点。这个调用会不断地向链表的后面部分递归,直到链表的末尾。
        head.next = self.removeElements(head.next,val)

        if head.val==val:
            return head.next
        else:
            return head

讨论:

关于递归,举个例子,比如链表[1,2,1,3],val=1,该参数传入到上面的递归法中计算,你会发现,虽然返回新的头节点是2,但是第一个1到2的指针并没有断开,关于这点,做以下讨论:

        在返回新链表头(值为2的节点)之前,原来的链表中第一个值为1的节点的next指针仍然指向值为2的节点。

        重要的是要理解,递归调用返回新链表头后,原来的链表中指向值为1的节点的引用可能不再被使用(除非有外部引用保持对该节点的引用)。递归函数返回新链表头后,调用该函数的上下文会继续使用这个新链表头,而不是原来的链表头。

        如果我们从外部观察这个递归过程,会看到如下情况:

  1. 初始时,外部引用指向原链表的头节点(值为1的节点)。
  2. 递归调用开始,逐步深入到链表内部。
  3. 当递归调用返回时,每个递归层级都会根据当前节点的值是否等于val来决定是否更新返回链表的头节点。
  4. 最终的递归调用返回后,原始链表中的第一个值为1的节点的next指针仍然指向值为2的节点,但是这个指向关系对于新的链表来说已经不再重要,因为新的链表头节点(值为2的节点)已经被返回,并且后续的链表结构(2 -> 3)已经按照预期被构建。

        在实际使用中,调用removeElements函数的代码会接收返回的新链表头,并可能用这个新链表头来更新它自己的引用,从而“断开”与原来链表中第一个值为1的节点的连接。但是,这个“断开”是在逻辑上的,即在新的链表结构中不再包含原来的头节点。实际的物理内存中,原来的链表结构仍然存在,直到没有任何引用指向它,从而被垃圾回收机制回收

        在大多数现代编程语言中,如果一个对象(在这里是 1节点)没有任何引用指向它,那么它就被认为是不可达的,并且最终会被垃圾回收器自动清理掉,以释放其占用的内存。

        重要的是,在函数内部,我们没有改变任何节点的next指针来“物理上”断开连接。我们只是返回了一个新的链表头,这个新的链表头指向的链表中不包含值为val的节点。

        因此,虽然第一个值为1的节点的next指针在递归过程中没有改变,但是返回的新链表已经不再包含这个节点,实现了从逻辑上移除该节点的效果。

  • 20
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值