给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
示例 1: 输入:[1,2,3,4,5] 输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2: 输入:[1,2,3,4,5,6] 输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
首先计算链表有多少个节点,然后可以计算出中间节点的位置。最终返回这个指针就好
class Solution:
def middleNode(self, head: ListNode) -> ListNode:
cur=head
count=0
while cur!=None:#循环计算链表长度
count+=1
cur=cur.next
mid=count//2#计算中间节点的位置
i=0
p=head
while i<mid:#再次遍历直到目标节点
p=p.next
i+=1
return p
官方答案:
快慢指针法:慢指针一次向右走一个,快指针一次走两个,这样每一次循环,快指针所在的节点位置都是慢指针的两倍,当快指针走到终点时,慢指针刚好位于中间节点
class Solution(object):
def middleNode(self, head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
思路:对于是空链表的情况,单独处理,返回原链表
对于非空链表,设置一个指针,从前往后遍历,每次递归记录上一次的结点,用当前结点的元素构造一个单独的结点,指向上一次的结点。
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
if head==None:
return head
cur=head
last=None
while cur!=None:
now=ListNode(cur.val)
now.next=last
last=now
cur=cur.next
return now
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2 输出: 1->2
示例 2:
输入: 1->1->2->3->3 输出: 1->2->3
双指针(快慢指针法):
错误点,我第一次写的代码,没有照顾到
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
if head==None:
return head
pre,cur=head,head.next
while cur:#快指针不为空的情况
if cur.val==pre.val:
cur=cur.next#让前一个结点直接指向下一个结点。断掉前一个节点与本节点的连续。
pre.next=cur
else:
pre=cur
cur=cur.next
return head
我的错误:链表基础薄弱:①不了解对于一个链表,如果有个指针指向它,指针向后走的过程中如果对链表的某个节点做了改动,那么最开始的链表就会被改动,最终只需要返回头结点即可。②忘记了删除节点的方法就是让该节点的上一个结点指向该结点的下一个结点。而我是让指针直接跳过了该结点,这样做对链表没有做任何改动。
给定一个链表,判断链表中是否有环。为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
示例1 示例2 示例3
超笨的方法。时间复杂度很高,但是这是为想了很久想到的:利用一个列表存储每个结点,对链表进行遍历的过程中,每次判断该结点是否与曾经的某个节点相同,如果相同则是环形链表,如果遍历结束都不存在,说明不是环形链表
class Solution(object):
def hasCycle(self, head):
if head== None:#
return False
if head.next==head:
return True
cur=head
p=[]
while cur:
if cur not in p:
p.append(cur)
cur=cur.next
else:
return True
return False
简化版
class Solution(object):
def hasCycle(self, head):
cur=head
p=[]
while cur:
if cur not in p:
p.append(cur)
cur=cur.next
else:
return True
return False
别人的方法:
好像两个人在一个操场上跑步,速度快的人一定会和速度慢的相遇(环),虽然可能走好几圈才会相遇,但是迟早会相遇,空间复杂度低。
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
作者:powcai
链接:https://leetcode-cn.com/problems/linked-list-cycle/solution/kuai-man-zhi-zhen-by-powcai-3/
编写一个程序,找到两个单链表相交的起始节点。如下面的两个链表:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
自己的方法:哈希表,遍历B链表中的所有节点,存储在哈希表中,然后遍历B链表,将B链表中的节点与哈希表中的节点对比。
其实我没学过哈希表,但是看起来和列表相似。我最开始用的是列表,遍历其中一个链表的所有节点,存储到列表中,但是运行出来的结果显示时间超过限制,将列表换成哈希表之后,问题得到解决,暂时不知道原理。
class Solution(object):
def getIntersectionNode(self, headA, headB):
item=set()#创建哈希表
p1=headA
while p1:#存储A链表中的所有节点
item.add(p1)
p1=p1.next
p2=headB
while p2:
if p2 in item:
return p2
p2=p2.next
return None
大牛的方法:拼接法
如下图所示,当链表A走到结尾时,将结尾指向链表B的头部。
对B链表做相同处理,那么两者连接成的环形链表是一样的。从任何一个链表的头部到两者相交的节点的长度相同。因此会同步到达相交节点(但我觉得这其中存在一个问题,链表的结构被改变了)
class Solution(object):
def getIntersectionNode(self, headA, headB):
ha, hb = headA, headB
while ha != hb:
ha = ha.next if ha else headB
hb = hb.next if hb else headA
return ha
作者:jyd
链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/solution/intersection-of-two-linked-lists-shuang-zhi-zhen-l/
删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
双节点法,只有一个节点以及链表为空是两种特殊情况,
class Solution(object):
def removeElements(self, head, val):
if head==None:
return head
pre,cur=head,head
while cur:#不包含单节点的情况
if cur.val==val:
pre.next=cur.next#不能处理头结点是目标结点的情况
cur=cur.next
else:
pre=cur
cur=cur.next
if head.val==val:#头结点是目标结点的情况
return head.next
return head
虚拟指针法:在head的前面加一个结点。这样就可以包含空链表或头结点的值就是目标值的情况
class Solution(object):
def removeElements(self, head, val):
dummy=ListNode(0)
pre=dummy
pre.next=head
cur=pre.next
while cur:#没有检查第一个节点
if cur.val==val:
pre.next=cur.next
cur=cur.next
else:
pre=cur
cur=cur.next
return dummy.next
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
#if l1 is None:
# return l2
#elif l2 is None:
# return l1
p1=l1
p2=l2
l=ListNode(0)#先构造一个虚拟结点
cur=l
while p1 and p2:
if p1.val>p2.val:
cur.next=p2
p2=p2.next
cur=cur.next
else:
cur.next=p1
p1=p1.next
cur=cur.next
'''下面是考虑到两个链表长度不同的情况'''
if p1:#第一个链表更长的情况
cur.next=p1
else:#第二个链表更长或者两者相同的情况
cur.next=p2
return l.next#虚拟结点的头结点是无效的,要省去
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2 输出: false
示例 2:
输入: 1->2->2->1 输出: true
回文链表指的是无论按照从前往后还是从后往前,读出来的都一样
思路:对于只有一个结点或空链表的情况单独处理,对于其他的链表,遍历每个结点存储到一个列表中(本来想用哈希表的,但是不知道怎么索引哈希表中的元素),之后再双向遍历列表中的结点。在遍历的过程中,如果存在两个结点对应的值不同就结束遍历,返回False。遍历结束如果还没有遇到不相等的情况,就返回True。
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if head is None:
return True
if head.next is None:
return True
p=[]#构造空列表,用于存储各个结点
cur=head
while cur:
p.append(cur)
cur=cur.next
n=len(p)
i=0
j=n-1
while i<=j:
if p[i].val!=p[j].val:
return False
i+=1
j-=1
return True
利用反转链表的方法:
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if head is None:
return True
if head.next is None:
return True
cur=head
last=None
while cur!=None:
now=ListNode(cur.val)
now.next=last
last=now
cur=cur.next
while head:
if head.val!=now.val:
return False
head=head.next
now=now.next
return True
对比发现,第一种方法的时间复杂度可空间复杂度都更低