datawhale组队学习笔记(2)链表

链表基础知识:

  1. 结构:
    ①逻辑结构:集合、线性结构(一对一)、树形结构(一对多)、图结构(多对多);
    ②存储结构:顺序存储(顺序表)、链式存储(链式表)、索引存储、散列存储。
    2.链表分类:
    ①单链表、双链表、循环链表(单/双)
    ②带头结点/不带头节点
    3.(单)链表操作:
    插入元素、删除元素、创建单链表(尾插法/头插法)
  2. 结构:
    ①逻辑结构:集合、线性结构(一对一)、树形结构(一对多)、图结构(多对多);
    ②存储结构:顺序存储(顺序表)、链式存储(链式表)、索引存储、散列存储。
    2.链表分类:
    ①单链表、双链表、循环链表(单/双)
    ②带头结点/不带头节点
    3.(单)链表操作:
    插入元素、删除元素、创建单链表(尾插法/头插法)
T1 合并两个有序链表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f3cNkgfh-1646065710091)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\image-20220218204710855.png)]

【法一】迭代(不明失败尝试):将list2中的元素插入到list中
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def mergeTwoLists(self, list1, list2):
        """
        :type list1: Optional[ListNode]
        :type list2: Optional[ListNode]
        :rtype: Optional[ListNode]
        """
        cur1=list1
        cur2=list2
        if not cur1:
            return list2
        elif not cur2:
            return list1
        
        while cur1 and cur2:
            if cur2.val<cur1.val: #只会出现在开头
                temp=list1
                list1=cur2 #出现了一个我不能理解的问题就是,它俩绑定了,甚至还影响了list2的值(用逐行return看出来的),但我不能理解为什么,只能知道以后别随便乱修改链表弄一大堆指针啥的,可能以后会明白吧??
                list1.next=temp
                cur1=list1
                cur2=cur2.next
            else: #cur2.val>=cur1.val
                if cur1.next:
                    if cur2.val>=cur1.next.val:
                        cur1=cur1.next
                    else: #cur1.val<= cur2.val <cur1.next.val
                        temp=cur1.next
                        new=cur2
                        cur1.next=new
                        new.next=temp
                        cur1=cur1.next
                        cur2=cur2.next
                else: #cur1到达list1的末尾
                    temp=cur1.next
                    new=cur2
                    cur1.next=new
                    new.next=temp
                    cur1=cur1.next
                    cur2=cur2.next
        return list1

整体思路是:
1.初始:使用两个指针,cur1指向list1首个结点,cur2指向list2首个节点
2.思路:将list2中的元素逐个插入到list1中(因此要逐个比较cur1和cur2指向元素的大小)
①cur1>cur2:
把cur2指向的元素插入到cur1指向的元素之前,cur1向前移动一位(即继续指向新list1的首个节点)
②cur1≤cur2:
再判断cur1.next与cur2的大小关系:
(a)cur1.next≤cur2:
将cur1指针后移一位
(b)cur1.next>cur2:
直接将cur2对应元素插入到cur1之后,cur1和cur2各后移一位

(中间套上了一层if cur1.next是因为要用到cur1.next.val,如果cur1已经指到末尾了会报错实际上这两个else写的是相同的)

问题:出现了一个我不能理解的问题就是,出现了不符合逻辑的绑定??甚至还混乱地影响了list2的值(用逐行return看出来的),反正结果就是cur2从list2上跑到新list1上去了,但我不理解为什么会跑,为什么会关联绑定…?只能知道以后别随便乱选择修改链表的方式,弄一大堆指针啥的,可能以后会明白吧??现在就现在想着创建新链表吧。。

【法二】迭代:创建一个新链表,轮流插入list1和list2的元素
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def mergeTwoLists(self, list1, list2):
        """
        :type list1: Optional[ListNode]
        :type list2: Optional[ListNode]
        :rtype: Optional[ListNode]
        """
        prehead=ListNode(-1) #创建一个新的空头结点
        prev=prehead #创建一个指针,指向空头结点(注意:这是指针,是可以修改东西的!)
        while list1 and list2:
            if list1.val<=list2.val:
                prev.next=list1 #指针所指位置的next
                list1=list1.next
            else:
                prev.next=list2
                list2=list2.next #指针所指位置的next
            prev=prev.next #指针后移一位,继续修改后面
        if list1 is None:
            prev.next=list2
        elif list2 is None:
            prev.next=list1
        #prev.next=list1 if list1 is not None else list2
        return prehead.next #虽然修改的一直都是prev.next,但是因为那是指针,所以它指到之处,确实被修改了

关于指针的问题,仔细想了想其实是这样:

① 像平时(例如之前做的数组题),当我们使用指针时,通常是作为 索引 ,然后根据 nums[cur]来对应所指的数据,自然就是可以修改的,这种指针并不涉及赋值问题,因为这样的指针是逻辑意义上的索引指针,而不是物理意义上的指针;

②关于赋值问题,比如平时我们 a=1, b=a, a=3 这样三句的结果是b=1,a=3,就是说赋值并不会造成关联,只是复制了一下这个值而已,这是普通的赋值语句,但是放到链表里,就要注意一些其他的东西了;

③在链表中用到的指针:

首先要想明白链表的数据结构,比如list1=[1,2,4],那么:

list1.val=1, list1.next=[2,4],

list1.next.val=2, list1.next.next=[4],

list1.next.next.val=4, list1.next.next.next=None

要注意,无论是list1还是list1.next这样,都是 ListNode()类型,可以大致类似于list1=ListNode(),也就是如果我们现在list1=cur,那么cur也是一个ListNode()类型,但实际上并没有新实例化一个类,而是把cur真正的指到了list1这个位置上,这样就不再是仅仅单纯的赋值语句了,而是真正物理意义上的指针,它修改的是list1的.val或者next,而不是仅仅复制,因此直接return原部分就可以得到修改后的了。

【法三】递归(与回溯)
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def mergeTwoLists(self, list1, list2):
        """
        :type list1: Optional[ListNode]
        :type list2: Optional[ListNode]
        :rtype: Optional[ListNode]
        """
        if not list1:
            return list2
        if not list2:
            return list1
        if list1.val<=list2.val:
            list1.next=self.mergeTwoLists(list1.next,list2)
            return list1
        else:
            list2.next=self.mergeTwoLists(list1,list2.next)
            return list2
T2.移除链表元素

在这里插入图片描述

【法一】迭代:依次判断每个元素

一个错误操作:(对于头节点的错误处理)

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        
        cur=head
        if head:
            if head.val==val:
                head=head.next
            while cur.next:
                if cur.next.val==val:
                    cur.next=cur.next.next
                else: #这个else一定要注意,如果没有else并且不缩进的话就错啦
                    cur=cur.next
        return head

正确操作:(对于头节点的正确处理)

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        head=ListNode(next=head) #加一个头结点
        pre=head #来一个指针
        while pre.next:
            if pre.next.val==val:
                pre.next=pre.next.next
            else:
                pre=pre.next
        return head.next
【法二】递归

1.错误尝试

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        head=ListNode(next=head) #创建一个空头结点
        cur=head #将指针指向空头节点(这在递归里是绝对不可以的,因为这个指针会被反复初始化)
        if not cur.next:
            return head.next
        if cur.next.val==val:
            cur.next=cur.next.next
            return self.removeElements(cur,val)
        else:
        	return self.removeElements(cur.next,val)
  • “ 将指针指向空头节点 ” 在递归里是绝对不可以的,因为这个指针会被反复初始化。

  • 迭代 vs 递归:

    ​ 迭代时一般来说是需要用到指针的,因为原head要用到return的时候,如果走下去就无法溯源了。递归时是不该用指针的,因为在每一次调用时指针会被反复初始化;然后因为它是递归+回溯,它最后是会回到开头的,所以其实也不需要弄个指针。

2.正确递归

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        if not head:
            return None
        head.next=self.removeElements(head.next,val)
        if head.val==val:
            return head.next
        else:
            return head
T3. 旋转链表

在这里插入图片描述

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def rotateRight(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        len=0
        cur=head
        if not cur:
            return head
        while cur.next:
            len+=1
            cur=cur.next
        else:
            len+=1
            cur.next=head
            cur=cur.next
        k_=k%len
        for i in range(len-k_-1):
            cur=cur.next
        head=cur.next
        cur.next=None
        return head
T4.删除排序链表中的重复元素Ⅰ

【法一】迭代
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        cur=head
        if not cur:
            return head
        while cur.next:
            if cur.val==cur.next.val:
                cur.next=cur.next.next
            else:
                cur=cur.next
        return head
【法二】递归
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head:
            return head
        if not head.next:
            return head
        head.next=self.deleteDuplicates(head.next) #其实基本属于是子问题重叠+最优子结构了
        if head.val==head.next.val:
            return head.next
        else:
            return head

比较类似于删除链表元素,递归解法就是“下一个是谁?”这样的问题,不过还算是首次盲写递归成功!

T5.删除排序链表中的重复元素Ⅱ

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mcByGDQU-1646065710097)(C:\Users\86133\AppData\Roaming\Typora\typora-user-images\image-20220219184319556.png)]

【法一】迭代(两次循环)(删除重复元素+删除特定元素)
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        a=[]
        #1 删除重复元素(只要删后面的就好,不需要加哑节点)
        cur=head
        if not cur:
            return head
        while cur.next:
            if cur.val==cur.next.val:
                a.append(cur.val)
                cur.next=cur.next.next
            else:
                cur=cur.next
        #2 删除特定元素(由于首个节点也有可能被删除,所以必须加一个哑节点)
        head=ListNode(next=head)
        cur=head
        while cur.next:
            if cur.next.val in a:
                cur.next=cur.next.next
            else:
                cur=cur.next
        return head.next

​ 其中,也可以很明显的感受到 “ 删除重复元素(只要删后面的就好,不需要加哑节点)” 和 “ 删除特定元素(由于首个节点也有可能被删除,所以必须加一个哑节点) ”的区别。

【法二】 迭代(一次循环)(删除重复元素+删除特定元素)
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        a=[]
        head=ListNode(next=head)
        cur=head
        if not cur.next:
            return cur.next
        while cur.next.next:
            if cur.next.val==cur.next.next.val:
                a.append(cur.next.val)
                cur.next=cur.next.next.next
                if not cur.next:
                    break
            elif cur.next.val in a:
                cur.next=cur.next.next
                if not cur.next:
                    break
            else:
                cur=cur.next
        if cur.next: #用else就更带感了
            if cur.next.val in a:
                cur.next=cur.next.next
        return head.next

​ 那个地方如果用else真的非常合适!因为while…else语句,指的就是:如果while循环是因为中途break而跳出,则不会执行else语句;如果while循环是因为条件不再符合而退出,则执行else语句。而恰好,这里如果 cur.next 还不是None,则最后会因为cur.next.next==None而退出,而如果cur.next变成None了,则循环判断语句本身会报错,因此必须使用break语句跳出,这样也正好与else应对应的条件相反。所以这里用while…else(配合内部break)非常合适。

【法三】迭代(一次双重循环)(察觉+循环直到消灭)
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        a=[]
        head=ListNode(next=head)
        cur=head
        while cur.next and cur.next.next:
            if cur.next.val==cur.next.next.val:
                a=cur.next.val
                while cur.next and cur.next.val==a:
                    cur.next=cur.next.next
            else:
                cur=cur.next
        return head.next

​ 这种方法除了提出了明智(相对于法二)的“循环直到消灭”之外,还有很重要的一点就是,它提出了cur.next and cur.next.next这种,也就是cur.next and cur.next.val==a这种避免出错的方式,也用不到break啦,自然也不是那么需要while…else语句这种啦。

【法四】递归
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return head
        if head.val!=head.next.val:
            head.next=self.deleteDuplicates(head.next)
        else:
            move=head.next
            while move and head.val==move.val: #这里在第一次其实是重复了的,但是更简洁了
                move=move.next
            return self.deleteDuplicates(move) #这就是主要区别,不用head.next=而用return就是删掉自己了
        return head
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值