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

目录

203.移除链表元素

思路

方法一:使用虚拟头结点删除元素

方法二:使用原头结点删除元素

心得收获 

707.设计链表

思路

方法一:不设虚拟头节点

方法二:使用虚拟头节点

方法三:使用双链表

心得收获

206.反转链表 

思路 

方法一: 双指针法

方法二:递归法 

心得收获


203.移除链表元素

思路

有两种解题办法:

  1. 使用虚拟头结点删除元素,使用虚拟头结点的好处是,在删除元素时,无需考虑对头结点特殊操作
  2. 使用原头结点进行操作

方法一:使用虚拟头结点删除元素

# 使用虚拟头结点进行删除元素
class Solution:
    def removeElement(self,head:ListNode,val:int) -> ListNode:
        dummyhead = ListNode(next=head)
        cur = dummyhead
        # 思考一下while cur和while cur.next的区别
        # 在这里我首先想到的是cur,老师给的解题是cur.next
        while cur.next:
            if cur.next.val == val:
                cur.next = cur.next.next
            else:
                cur = cur.next
        return dummyhead.next

方法二:使用原头结点删除元素

class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        # 我们要取头结点,所以头结点一定不能为空
        while head and head.val == val:
            head = head.next
        cur = head
        while cur and cur.next:
            if cur.next.val == val:
                cur.next = cur.next.next
            else:
                cur = cur.next
        return head

心得收获 

在使用虚拟头结点时,有几个问题要注意:

  • 使用虚拟头结点while循环的条件我设为cur,解题思路是用cur.next,使用cur的话,在cur走到链表末尾的时候会报错,因为链表末尾指向NULL,再用cur.next.val会提示错误,因为NULL没有val这个属性,详见下图:
    1cbca0176ca049b8b8b8c9fef890d67a.png
  • 为什么使用头结点和不适用头结点while的条件不一样呢?
    在不使用头结点时,我们没法判断cur以及cur.next(要判断cur.next是因为我们要取下一个节点的值比较,如果下一个节点不存在,我们取值相当于操作空指针,就会报错。和我上面的报错一样)是不是存在 。
    在使用虚拟头结点时,cur先指向我们的虚拟头结点,此时cur一定是存在的,所以只需要判断cur.next即可,下一次循环同理,此时cur.next 变为了cur,如果存在的话,只需要判断cur.next即可
  • 使用虚拟头结点时,为什么cur不是指向dummyhead.next?
    首先我们要明白我们删除节点时,一定要知道它的上一个节点信息(单链表只能拿到下一个节点信息,获取不到上一个节点是什么),如果我们的cur指向的是原头结点,那删除原头结点时,我们没有办法知道头结点的上一个节点是什么
    ba6c421567e04f5da5ae12d910676e82.png
  • 在while循环中,为什么cur = cur.next要写在else里面?为什么不是if条件判断完之后,直接
    cur = cur.next?

    如果写成和if条件一个层级的话,那在删除元素之后,我们会跳过要删除元素后面的节点判断
    4028d586057548e59c477e14620c1098.png

707.设计链表

思路

我没有使用虚拟头结点设计的链表,想好思路

方法一:不设虚拟头节点

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

class MyLinkedList:
    def __init__(self) -> None:
        self.head = None
        self.size = 0
    # 获取节点的值
    def get(self,index:int) -> int:
        # 因为index是从0开始的,所以此处判断要大于等于链表长度
        # 不然会报错,因为超出链表长度,最后会操作空指针
        if index < 0 or index >= self.size:
            return -1
        cur = self.head
        while index:
            cur = cur.next
            index -= 1
        return cur.val
    # 添加链表头节点
    def addAtHead(self,val:int) -> None:
        if self.size == 0:
            self.head = ListNode(val)
        else:
            new_node = ListNode(val,self.head)
            # 此处忘记加上self,类中调用变量或函数,一定一定一定要加self!!!
            self.head = new_node
        self.size += 1
    # 在链表尾部添加元素
    def addAtTail(self,val:int) -> None:
        if self.size == 0:
            # 其实这里可以调用添加头节点函数
            self.head = ListNode(val)
        # 此处忘记加上else判断,导致添加头节点之后,后面的代码又执行了一遍
        else:
            cur = self.head
            while cur.next:
                cur = cur.next
            cur.next = ListNode(val)
        self.size += 1
    # 添加链表元素
    def addAtIndex(self,index:int,val:int) -> None:
        if index > self.size:
            return
        elif index <= 0:
            # 此处最坑,因为添加完头节点之后,self.size已经加1,然后又继续执行后面的代码
            # 导致self.size又加1
            # 所以加上return操作,在添加头节点之后直接结束,判断尾节点添加同理
            self.addAtHead(val)
            return
        elif index == self.size:
            self.addAtTail(val)
            return
        else:
            cur = self.head
            while index-1:
                cur = cur.next
                index -= 1
            new_node = ListNode(val,cur.next)
            cur.next = new_node
        self.size += 1
    # 删除链表元素
    def deleteAtIndex(self,index:int) -> None:
        if index == 0:
            self.head = self.head.next
        elif index >= self.size or index < 0:
            return
        else:
            cur = self.head
            # 此处要index-1,因为index是从0开始的
            while index-1:
                cur = cur.next
                index -= 1
            cur.next = cur.next.next

        self.size -= 1

方法二:使用虚拟头节点

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

class MyLinkedList:
    def __init__(self) -> None:
        self.dummyhead = ListNode()
        self.size = 0
    # 获取节点的值
    def get(self,index:int) -> int:
        if index < 0 or index >= self.size:
            return -1
        cur = self.dummyhead
        while index:
            cur = cur.next
            index -= 1
        return cur.next.val
    # 添加链表头节点
    def addAtHead(self,val:int) -> None:
        new_node = ListNode(val)
        if self.size == 0:
            self.dummyhead.next = new_node
        else:
            new_node.next = self.dummyhead.next
            self.dummyhead.next = new_node
        self.size += 1
    # 在链表尾部添加元素
    def addAtTail(self,val:int) -> None:
        new_node = ListNode(val)
        if self.size == 0:
            self.dummyhead.next = new_node
        else:
            cur = self.dummyhead
            while cur.next:
                cur = cur.next
            cur.next = new_node
        self.size += 1
    # 添加链表元素
    def addAtIndex(self,index:int,val:int) -> None:
        if index > self.size:
            return
        elif index == self.size:
            self.addAtTail(val)
            return
        else:
            new_node = ListNode(val)
            cur = self.dummyhead
            while index:
                cur = cur.next
                index -= 1
            new_node.next = cur.next
            cur.next = new_node
        self.size += 1
    # 删除链表元素
    def deleteAtIndex(self,index:int) -> None:
        if index < 0 or index >= self.size:
            return
        else:
            cur = self.dummyhead
            while index:
                cur = cur.next
                index -= 1
            cur.next = cur.next.next

        self.size -= 1

方法三:使用双链表

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

class MyLinkedList:
    def __init__(self) -> None:
        self.head = None
        self.tail = None
        self.size = 0
    def get(self,index:int) -> None:
        if index < 0 or index >= self.size:
            return -1
        else:
            if index > self.size // 2:
                cur = self.tail
                for i in range(self.size-index-1):
                    cur = cur.pre
            else:
                cur = self.head
                for i in range(index):
                    cur = cur.next
            return cur.val
    # 添加链表头节点
    def addAtHead(self,val:int) -> None:
        new_node = ListNode(val)
        if self.size == 0:
            self.head = new_node
            self.tail = new_node
        else:
            new_node.next = self.head
            # 这里忘记写把原头节点的向前的指针给更改
            self.head.pre = new_node
            self.head = new_node
        self.size += 1
    # 在链表尾部添加元素
    def addAtTail(self,val:int) -> None:
        new_node = ListNode(val)
        if self.size == 0:
            self.head = new_node
            self.tail = new_node
        else:
            cur = self.tail
            cur.next = new_node
            # 这里忘记把新尾结点的向前指针值更改
            new_node.pre = cur
            self.tail = new_node
        self.size += 1
    # 添加链表中间元素
    def addAtIndex(self,index:int,val:int) -> None:
        if index > self.size:
            return
        elif index <= 0:
            self.addAtHead(val)
            return
        elif index == self.size:
            self.addAtTail(val)
            return
        else:
            new_node = ListNode(val)
            if index > self.size // 2:
                cur = self.tail
                for i in range(self.size-index-1):
                    cur = cur.pre
            else:
                cur = self.head
                for i in range(index):
                    cur = cur.next
            new_node.next = cur
            new_node.pre = cur.pre
            cur.pre.next = new_node
            cur.pre = new_node

        self.size += 1
    # 删除链表元素
    def deleteAtIndex(self,index:int) -> None:
        if index < 0 or index >= self.size:
            return
        elif index == 0:
            if self.size == 1:
                self.head = None
                self.tail = None
            else:
                self.head = self.head.next
                self.head.pre = None
        elif index == self.size-1:
            self.tail = self.tail.pre
            self.tail.next = None
        else:
            if index > self.size // 2:
                cur = self.tail
                for _ in range(self.size-index-1):
                    cur = cur.pre
            else:
                cur = self.head
                for _ in range(index):
                    cur = cur.next
            cur.pre.next = cur.next
            cur.next.pre = cur.pre
        self.size -= 1        

心得收获

目前只写了,不设虚拟头节点的代码,遇到的问题也都注释在代码里,这里就不过多赘述,后面写虚拟头节点和双链表遇到问题,再更新
更新:使用虚拟头结点写下来基本很顺畅,但是这次总是忘记在while循环中对索引减小
更新:使用双指针写下来更顺畅一些,对链表的操作更熟悉了
加油吖!相信会越来越熟悉

206.反转链表 

思路 

  1. 使用双指针
  2. 使用递归,目前对递归没有很深入的理解,先不做叙述,以免带偏

方法一: 双指针法

# 双指针法
class Solution:
    def reverseList(self,head:ListNode) -> ListNode:
        pre = None
        cur = head
        while cur:
            tmp = cur.next
            cur.next = pre
            pre = cur
            cur = tmp

        return pre

方法二:递归法 

# 递归法
class Solution:
    def reverseList(self,head:ListNode) -> ListNode:
        self.digui(head,None)
    def digui(self,cur:ListNode,pre:ListNode) -> ListNode:
        if cur == None:
            return pre
        tmp = cur.next
        cur.next = pre
        return self.digui(tmp,pre)

心得收获

为什么要设双指针并初始化,首先要反转链表,当前节点cur的cur.next方向要指向前一个节点pre,那头节点的反转之后变成尾节点指向None,所以pre初始化为None。

另外要注意断开连接之后,要使用的节点要提前保存到临时变量,以免断开连接找不到该节点。

关于递归解法,可以看卡哥视频,完全能理解到位。仅限本题思路

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值