链表基础知识:

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

T1 合并两个有序链表

【法一】迭代(不明失败尝试):将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=list1cur2=list2if not cur1:return list2elif not cur2:return list1while cur1 and cur2:if cur2.val<cur1.val: #只会出现在开头temp=list1list1=cur2 #出现了一个我不能理解的问题就是,它俩绑定了,甚至还影响了list2的值(用逐行return看出来的),但我不能理解为什么,只能知道以后别随便乱修改链表弄一大堆指针啥的,可能以后会明白吧??list1.next=tempcur1=list1cur2=cur2.nextelse: #cur2.val>=cur1.valif cur1.next:if cur2.val>=cur1.next.val:cur1=cur1.nextelse: #cur1.val<= cur2.val <cur1.next.valtemp=cur1.nextnew=cur2cur1.next=newnew.next=tempcur1=cur1.nextcur2=cur2.nextelse: #cur1到达list1的末尾temp=cur1.nextnew=cur2cur1.next=newnew.next=tempcur1=cur1.nextcur2=cur2.nextreturn 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 #指针所指位置的nextlist1=list1.nextelse:prev.next=list2list2=list2.next #指针所指位置的nextprev=prev.next #指针后移一位,继续修改后面if list1 is None:prev.next=list2elif list2 is None:prev.next=list1#prev.next=list1 if list1 is not None else list2return 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 list2if not list2:return list1if list1.val<=list2.val:list1.next=self.mergeTwoLists(list1.next,list2)return list1else: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=headif head:if head.val==val:head=head.nextwhile cur.next:if cur.next.val==val:cur.next=cur.next.nextelse: #这个else一定要注意,如果没有else并且不缩进的话就错啦cur=cur.nextreturn 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.nextelse:pre=pre.nextreturn 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.nextif cur.next.val==val:cur.next=cur.next.nextreturn 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 Nonehead.next=self.removeElements(head.next,val)if head.val==val:return head.nextelse: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=0cur=headif not cur:return headwhile cur.next:len+=1cur=cur.nextelse:len+=1cur.next=headcur=cur.nextk_=k%lenfor i in range(len-k_-1):cur=cur.nexthead=cur.nextcur.next=Nonereturn 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=headif not cur:return headwhile cur.next:if cur.val==cur.next.val:cur.next=cur.next.nextelse:cur=cur.nextreturn 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 headif not head.next:return headhead.next=self.deleteDuplicates(head.next) #其实基本属于是子问题重叠+最优子结构了if head.val==head.next.val:return head.nextelse:return head

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

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

【法一】迭代(两次循环)(删除重复元素+删除特定元素)
# 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=headif not cur:return headwhile cur.next:if cur.val==cur.next.val:a.append(cur.val)cur.next=cur.next.nextelse:cur=cur.next#2 删除特定元素(由于首个节点也有可能被删除,所以必须加一个哑节点)head=ListNode(next=head)cur=headwhile cur.next:if cur.next.val in a:cur.next=cur.next.nextelse:cur=cur.nextreturn 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=headif not cur.next:return cur.nextwhile cur.next.next:if cur.next.val==cur.next.next.val:a.append(cur.next.val)cur.next=cur.next.next.nextif not cur.next:breakelif cur.next.val in a:cur.next=cur.next.nextif not cur.next:breakelse:cur=cur.nextif cur.next: #用else就更带感了if cur.next.val in a:cur.next=cur.next.nextreturn 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=headwhile cur.next and cur.next.next:if cur.next.val==cur.next.next.val:a=cur.next.valwhile cur.next and cur.next.val==a:cur.next=cur.next.nextelse:cur=cur.nextreturn 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 headif head.val!=head.next.val:head.next=self.deleteDuplicates(head.next)else:move=head.nextwhile move and head.val==move.val: #这里在第一次其实是重复了的,但是更简洁了move=move.nextreturn self.deleteDuplicates(move) #这就是主要区别,不用head.next=而用return就是删掉自己了return head

datawhale组队学习笔记(2)链表相关推荐

  1. datawhale组队学习笔记(3)树

    1 树的基本知识 1.1 概念 树,结合了链表与图. ①单链表:一个数据域+一个指针域:树:一个数据域+多个指针域. ②树是无环连通图. 1.2 定义 树是N个节点的有限集合,N=0为空树,N> ...

  2. DQN相关知识总结及演员-评论员算法介绍(DataWhale组队学习笔记)

    DQN基本概念及相关技巧 DQN(Deep Q-network)即深度Q网络,通过结合神经网络技术和价值函数近似,采用目标网络和经历回放的方法来进行网络的训练. 价值函数近似 在面对现实中的强化学习任 ...

  3. 第8期Datawhale组队学习计划

    第8期Datawhale组队学习计划马上就要开始啦 这次共组织15个组队学习,涵盖了AI领域从理论知识到动手实践的内容 按照下面给出的最完备学习路线分类,难度系数分为低.中.高三档,可以按照需要参加 ...

  4. 组队学习笔记Task1:论文数据统计

    数据分析第一次组队学习笔记--Lizzy @Datawhale Task1:论文数据统计 学习主题:论文数量统计(数据统计任务),统计2019年全年,计算机各个方向论文数量: 学习内容:赛题理解.Pa ...

  5. Datawhale组队学习周报(第047周)

    本周报总结了从 2021年01月03日至2022年01月09日,Datawhale组队学习的运行情况,我们一直秉承"与学习者一起成长的理念",希望这个活动能够让更多的学习者受益. ...

  6. Datawhale组队学习周报(第041周)

    本周报总结了从 11月22日至11月28日,Datawhale组队学习的运行情况,我们一直秉承"与学习者一起成长的理念",希望这个活动能够让更多的学习者受益. 第 31 期组队学习 ...

  7. Datawhale组队学习周报(第040周)

    本周报总结了从 11月15日至11月21日,Datawhale组队学习的运行情况,我们一直秉承"与学习者一起成长的理念",希望这个活动能够让更多的学习者受益. 第 31 期组队学习 ...

  8. Datawhale组队学习周报(第038周)

    本周报总结了从 11月01日至11月07日,Datawhale组队学习的运行情况,我们一直秉承"与学习者一起成长的理念",希望这个活动能够让更多的学习者受益. 第 30 期组队学习 ...

  9. Datawhale组队学习周报(第035周)

    希望开设的开源内容 目前Datawhale的开源内容分为两种:第一种是已经囊括在我们的学习路线图内的Datawhale精品课,第二种是暂未囊括在我们的学习路线图内的Datawhale打磨课.我们根据您 ...

最新文章

  1. iis 程序池设置及详解-20180720
  2. 前嗅ForeSpider教程:网站登录配置
  3. Climbing Stairs - Print Path
  4. wifi名称可以有空格吗_收购公司后可以变更公司名称吗,变更公司名称和股权如何处理?...
  5. python制作文本编辑器_Python小实战:制作文本编辑器
  6. JS高级——手写call()、apply()、bind()
  7. 清远机器人编程_「新时代好少年」清远学生研发“灭火装置”获实用新型专利...
  8. 若依(RuoYi)如何不登录直接访问?
  9. 自己定制树莓派Linux内核的步骤
  10. 2020华为软件精英挑战赛-有向图找环
  11. html5上传steam,Steam.html · savfile/shencore.github.io - Gitee.com
  12. RMAN传输表空间迁移数据
  13. 管中窥豹SPDK RBD bdev 模块
  14. 计算机远程桌面修复,大师教您Win10远程桌面连接的修复手段
  15. 图片不能置于底层怎么办_ps怎么把图片置于底层
  16. HTTP取消SSL认证
  17. [校内自测] Incr (LIS+智商)
  18. 二、Git本地仓库基本操作——创建Git仓库、提交更新或删除文件
  19. xv6操作系统源码阅读之init进程
  20. 如何在Texpad中使用SJTUThesis模版

热门文章

  1. Mac本如何运营php框架,1、Mac系统下搭建thinkPHP框架环境
  2. 对广晟有色的数据分析
  3. Python-基础-时间日期处理小结
  4. /proc/net/sockstat 里的信息是什么意思?
  5. 地址总线与内存大小的关系(待续…)
  6. C#生成CHM文件(中级篇)
  7. 今年的假期挺长的~~~
  8. postfix文档修正
  9. MyEclipse 清理项目缓存的几大方法
  10. C#关键字详解第二节