代码随想录算法训练营第三天|203移除链表元素 707设计链表 206反转链表

203. 移除链表元素

题意:删除链表中等于给定值 val 的所有节点。

示例 1: 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]

示例 2: 输入:head = [], val = 1 输出:[]

示例 3: 输入:head = [7,7,7,7], val = 7 输出:[]

链表一直是处理的不太好的数据结构,太久没处理过,第一次做链表题容易直接定义一个临时的cur节点变量等于需要判断的节点,由于是在单向链表的场景下,如果判断出该节点需要被删除,那么我们无法对上一个节点进行操作(需要让【该删除的节点】的上一个节点指向【该删除的节点】的下一个节点),这样就无法继续了。

所以思路就是上述括号这样,但是代码的完成和完整性需要考虑的仔细一点,在代码实现的时候,应该让临时节点cur的next指向被判断的节点,也就是cur应该作为【被判断节点】的前一个节点,判断删除的时候,直接让cur节点next绕过被删除节点即可。(python自动释放,C++手动释放被删除节点)

以下为代码的实现:

# Definition for singly-linked list.

# class ListNode:

#     def __init__(self, val=0, next=None):

#         self.val = val

#         self.next = next

class Solution:

    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:

        while head and head.val == val:

            head = head.next #当头部节点为被删除节点,头部改为下一个节点,直到不是被删除节点(可能全是,最后剩下空链表)



        cur = ListNode(next = head)



        while cur.next:

            if cur.next.val == val:

                cur.next = cur.next.next

            else:

                cur = cur.next



        return head

代码随想录提供【虚拟头结点】的方法:

那么可不可以 以一种统一的逻辑来移除 链表的节点呢。

其实可以设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了。

理解下来发现和我自己的实现也是大同小异的思路,但是更加规范化了,也更公式化,有利于后续类似题目提高做题速度,记录一下:

虚拟头节点法

# Definition for singly-linked list.# class ListNode:

#     def __init__(self, val=0, next=None):

#         self.val = val

#         self.next = next



class Solution:

    

def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:

        # 创建虚拟头部节点以简化删除过程

        dummy_head = ListNode(next = head)

        

        # 遍历列表并删除值为val的节点

        current = dummy_head

        while current.next:

            if current.next.val == val:

                current.next = current.next.next

            else:

                current = current.next

        

        return dummy_head.next

707.设计链表

题意:

在链表类中实现这些功能:

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

707示例

思路还是使用推荐的虚头部节点,在ListNode结构中设置val和next,在List中设置虚头部dummyhead和size(为了判断索引是否正确)。

Debug问题主要出在:

  1. 声明cur节点的方式不太好,使用了构造函数,反而导致出现很多问题,还是掌握的不太好,直接令其等于dummyhead其实就可以了。至于事等于dummyhead还是dummyhead.next,根据函数的作用和目标来理解即可。
  2. 对数据结构和对象的构造函数、属性、self的使用等不熟练,这一题强化了自己的掌握度。
  3. 虽然最后debug出来了,但还是有很多习惯不太好,应借鉴规范代码改进。
  4. Debug中的一个问题补充一段AI的说明改正:ListNode=None 出现在 MyLinkedList 类的构造函数中,这是一个错误的用法。ListNode 应该是一个类,而不是一个值。您可能想要将 None 赋给 dummyhead.next,表示链表开始时为空。

MyLinkedList 类中,dummyhead 是一个哑节点(也称为哨兵节点),它不是链表的一部分,但是作为链表的头部,使得我们可以统一处理头部和中间节点的添加和删除操作。哑节点不存储任何实际的数据,它的 next 指针指向链表的第一个实际节点。

以下为代码实现:

class ListNode:
   def __init__(self, val=0, next=None):
       self.val = val
       self.next = next


class MyLinkedList:
   def __init__(self):
   #def __init__(self, ListNode=None)   # 自己瞎补充的None导致debug了很久。。也没想起来为啥自己这么写
       # self.head = ListNode          # 根据代码随想录的指引,还是使用虚头部,使用了虚头部后head多余了
       self.dummyhead = ListNode()    # 初始化0
       self.size = 0

   def get(self, index: int) -> int:
       if index<0 or index>=self.size:
           return -1
       # cur = ListNode(next=self.dummyhead.next)  # 直接使用 self.dummyhead 而不是创建一个新的 ListNode 实例。
       cur = self.dummyhead.next
       for i in range(index):
           cur = cur.next
       #return cur.next.val
       return cur.val

   def addAtHead(self, val: int) -> None:
       newHead = ListNode(val=val)
       newHead.next = self.dummyhead.next
       self.dummyhead.next = newHead
       self.size += 1

   def addAtTail(self, val: int) -> None:
       #cur = ListNode(next=self.dummyhead.next)   #直接使用 self.dummyhead 而不是创建一个新的 ListNode 实例。
       cur = self.dummyhead.next
       #for i in range(self.size):             # 找尾部直接用while不用for
       #    cur = cur.next
       while cur.next:
           cur = cur.next
       newTail = ListNode(val=val)
       #cur.next.next = newTail
       cur.next = newTail
       self.size += 1

   def addAtIndex(self, index: int, val: int) -> None:
       if index > self.size:
           return
       #cur = ListNode(next=self.dummyhead.next)   #直接使用 self.dummyhead 而不是创建一个新的 ListNode 实例。
       cur = self.dummyhead
       for i in range(index):
           cur = cur.next
       newNode = ListNode(val, cur.next)
       cur.next = newNode
       self.size += 1

   def deleteAtIndex(self, index: int) -> None:
       if index<self.size:
           #cur = ListNode(next=self.dummyhead.next) #直接使用 self.dummyhead 而不是创建一个新的 ListNode 实例。
           cur = self.dummyhead
           for i in range(index):
               cur = cur.next
           cur.next = cur.next.next
           self.size -= 1

# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)

规范代码:

class ListNode:

    def __init__(self, val=0, next=None):

        self.val = val

        self.next = next

        class MyLinkedList:

    def __init__(self):

        self.dummy_head = ListNode()

        self.size = 0



    def get(self, index: int) -> int:

        if index < 0 or index >= self.size:

            return -1

        

        current = self.dummy_head.next

        for i in range(index):

            current = current.next

            

        return current.val



    def addAtHead(self, val: int) -> None:

        self.dummy_head.next = ListNode(val, self.dummy_head.next)

        self.size += 1



    def addAtTail(self, val: int) -> None:

        current = self.dummy_head

        while current.next:

            current = current.next

        current.next = ListNode(val)

        self.size += 1



    def addAtIndex(self, index: int, val: int) -> None:

        if index < 0 or index > self.size:

            return

        

        current = self.dummy_head

        for i in range(index):

            current = current.next

        current.next = ListNode(val, current.next)

        self.size += 1



    def deleteAtIndex(self, index: int) -> None:

        if index < 0 or index >= self.size:

            return

        

        current = self.dummy_head

        for i in range(index):

            current = current.next

        current.next = current.next.next

        self.size -= 1



# Your MyLinkedList object will be instantiated and called as such:# obj = MyLinkedList()# param_1 = obj.get(index)# obj.addAtHead(val)# obj.addAtTail(val)# obj.addAtIndex(index,val)# obj.deleteAtIndex(index)

基本没有大问题,和规范代码思路基本一致。

这次没时间写多一个双向链表的实现了,先将规范代码记录,理解之后后面抽一天回来闭卷补了。

(版本二)双链表法

class ListNode:

    def __init__(self, val=0, prev=None, next=None):

        self.val = val

        self.prev = prev

        self.next = next

class MyLinkedList:

    def __init__(self):

        self.head = None

        self.tail = None

        self.size = 0



    def get(self, index: int) -> int:

        if index < 0 or index >= self.size:

            return -1

        

        if index < self.size // 2: # 整数除法,确保得出的数是整数

            current = self.head

            for i in range(index):

                current = current.next

        else:

            current = self.tail

            for i in range(self.size - index - 1):

                current = current.prev

                

        return current.val



    def addAtHead(self, val: int) -> None:

        new_node = ListNode(val, None, self.head)

        if self.head:

            self.head.prev = new_node

        else:

            self.tail = new_node

        self.head = new_node

        self.size += 1



    def addAtTail(self, val: int) -> None:

        new_node = ListNode(val, self.tail, None)

        if self.tail:

            self.tail.next = new_node

        else:

            self.head = new_node

        self.tail = new_node

        self.size += 1



    def addAtIndex(self, index: int, val: int) -> None:

        if index < 0 or index > self.size:

            return

        

        if index == 0:

            self.addAtHead(val)

        elif index == self.size:

            self.addAtTail(val)

        else:

            if index < self.size // 2:

                current = self.head

                for i in range(index - 1):

                    current = current.next

            else:

                current = self.tail

                for i in range(self.size - index):

                    current = current.prev

            new_node = ListNode(val, current, current.next)

            current.next.prev = new_node

            current.next = new_node

            self.size += 1



    def deleteAtIndex(self, index: int) -> None:

        if index < 0 or index >= self.size:

            return

        

        if index == 0:

            self.head = self.head.next

            if self.head:

                self.head.prev = None

            else:

                self.tail = None

        elif index == self.size - 1:

            self.tail = self.tail.prev

            if self.tail:

                self.tail.next = None

            else:

                self.head = None

        else:

            if index < self.size // 2:

                current = self.head

                for i in range(index):

                    current = current.next

            else:

                current = self.tail

                for i in range(self.size - index - 1):

                    current = current.prev

            current.prev.next = current.next

            current.next.prev = current.prev

        self.size -= 1





# Your MyLinkedList object will be instantiated and called as such:# obj = MyLinkedList()# param_1 = obj.get(index)# obj.addAtHead(val)# obj.addAtTail(val)# obj.addAtIndex(index,val)# obj.deleteAtIndex(index)

206.反转链表

题意:反转一个单链表。

示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL

由于正常的思路过于好想,新建一个链表的思路肯定不是所考察的内容,所以第一时间也懒得去实现了。直接看到了今天的提示:双指针和递归。

试着自己实现双指针的思路:

一个prev指针,一个cur指针,思路肯定是让cur指向prev,然后再往后平移,依次让节点指向往前指,但是由于只看了双指针法的字面意思,一直认定只需要两个指针就能实现,所以虽然自己想到了用tem存储【原】cur.next的位置,但还是限制自己去用这个方法存放,后面发现好像无论如何会使得链表中断,忍不住看了解析,发现确实还是用了temp,看来这个用来存放的节点变量不能当作指针,有点苦笑不得。那么实现就简单了,明白思路后两分钟就速通了代码:

# Definition for singly-linked list.

# class ListNode:

#     def __init__(self, val=0, next=None):

#         self.val = val

#         self.next = next

class Solution:

    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:

        prev = None

        cur = head

        while cur:

            tem = cur.next

            cur.next = prev

            prev = cur

            cur = tem

        return prev

看了视频,卡哥讲的太清晰易懂了,把递归也实现一下。

# Definition for singly-linked list.

# class ListNode:

#     def __init__(self, val=0, next=None):

#         self.val = val

#         self.next = next

class Solution:

    def reverse(self, prev:ListNode, cur:ListNode) -> ListNode: #在类中新定义一个def,定义这行括号里需要self,用于构造函数,由于返回的是一个ListNode,括号后需要->ListNode

        if not cur:

            return prev # 递归返回条件是cur为空

        tem = cur.next

        cur.next = prev

        return self.reverse(cur, tem) # 调用递归函数需要self.reverse() 括号里不需要带self

   

   

    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:

        prev = None

        cur = head

        return self. reverse(prev, cur) # 调用类中其他def需要用self

       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值