一:LeetCode24. 两两交换链表中的节点
1:题目描述(24. 两两交换链表中的节点)
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
2:解题思路
这道题首先需要确认的一点是,交换的是两个节点,不是两个节点的值。
我们使用虚拟头节点,然后定义临时指针cur,指向虚拟头节点,难点在于每次循环时cur指针的位置,以及修改节点next指向的顺序。
这里我犯了一个逻辑思维小错误,就是自己以为,每个节点只能有一个节点指向自己和自己只能指向一个节点。后面才反应过来,节点自己的next指针只能指向一个节点,但是可以有多个节点的next指针指向自己。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def swapPairs(self, head):
mydeay_node = ListNode(next=head) # 定义虚拟头节点,next指向链表头节点
cur = mydeay_node # 定义临时指针,指向虚拟头节点
while cur.next != None and cur.next.next != None:
temp1 = cur.next # 定义一个临时指针,指向当前节点的下一个节点
temp2 = cur.next.next # 定义一个临时指针,指向当前节点的下一个节点的下一个节点
# 下面交换相邻节点,修改节点的next指向
temp1.next = temp2.next
temp2.next = temp1
cur.next = temp2
# 修改临时指针cur
cur = cur.next.next
return mydeay_node.next
二、LeetCode19.删除链表的倒数第N个节点
1:题目描述(19.删除链表的倒数第N个节点)
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
2:解题思路
我们只需要找到倒数第n个节点的前面一个节点(倒数第n+1个节点),将倒数第n+1个节点的next指向倒数第n个节点的下一个节点(倒数第n-1个节点),即可将倒数第n个节点删除。
解法一:先统计出链表的长度,然后使用for循环,遍历链表,找到倒数第n+1个节点,结束循环,修改倒数第n+1个节点的next指针。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head, n):
# 删除倒数第n个节点,我们需要先找到倒数第n+1个节点
mydeay_node = ListNode(next=head) # 定义一个虚拟头节点
cur, pre = mydeay_node, mydeay_node # 定义两个临时指针,cur用来遍历链表统计链表的长度,pre用来找到倒数第n+1个节点
count = 0 # 统计链表长度的变量
while cur.next != None: # 当res指针对应节点的下一个节点不为None,说明还未遍历到尾节点
count += 1 # 链表长度+1
cur = cur.next # cur指向下一个节点
# 统计完链表的长度了,我们需要找到倒数第n个节点的前一个节点
for i in range(count-n): # 遍历链表,找到倒数第n+1个节点
pre = pre.next
pre.next = pre.next.next # 找到后,将倒数第n+1个节点的next指针指向它下一个节点的下一个节点(第n-1个节点),即可删除倒数第n个节点
return mydeay_node.next
解法二:使用双指针-快慢指针
还是添加一个虚拟头节点,快慢指针均指向链表的虚拟头节点,快指针先走n+1步,然后慢指针再出发,慢指针与快指针同时移动,当快指针指向为None时,慢指针到达了倒数第n+1个节点,然后修改慢指针指向的节点的next指针即可。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head, n):
# 使用双指针-快慢指针
# 快指针先走n+1步,然后快慢指针同时移动,直到快指针指向了None,慢指针移动到了倒数第n个节点的前一个节点
mydeay_node = ListNode(next=head) # 定义虚拟头节点
fast, slow = mydeay_node, mydeay_node # 定义快慢指针
for i in range(n+1):
fast = fast.next # 快指针向后走n+1步
while fast != None: # 快指针不为空,同时移动快慢指针
fast = fast.next
slow = slow.next
slow.next = slow.next.next # 快指针为空,慢指针到了倒数第n个节点的前一个节点,让慢指针的next指向慢指针的下一个节点的下一个节点
return mydeay_node.next
三、面试题 02.07. 链表相交
1:题目描述(面试题 02.07. 链表相交)
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
图示两个链表在节点 c1
开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
2:解题思路
解法一:使用双指针,分别指向两个链表的头节点,然后同时向移动一个节点,当有指针指向为None时,则指向另外一个链表的头节点,如果两个链表有相交的节点,则两个指针一定会相遇。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA, headB):
# 使用双指针
# 两个指针分别从两个链表的头节点出发,到达各自链表的尾节点后,指向另外一个链表的头节点,继续往后走,这样两个指针会在链表的相交点相遇
if headA is None or headB is None:
return None
cur1 = headA
cur2 = headB
while cur1 != cur2:
if cur1 == None: # cur1等于None,则将cur1指向链表B的头节点
cur1 = headB
else: # cur1不等于None,则继续向后移动
cur1 = cur1.next
if cur2 == None: # cur2等于None,则将cur2指向链表A的头节点
cur2 = headA
else: # cur2不等于None,则继续向后移动
cur2 = cur2.next
return cur1
解法二:也是使用双指针,不过跟解法一不同的是,先分别统计两个链表的长度,然后将两个链表尾部对齐,较长链表的指针先走,走到与较短链表长度一致的位置,两个链表的指针再同时向后移动,直到两个指针相等,则表示找到了相交点。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA, headB):
# 先统计两个链表的长度,将两个链表尾部对齐,然后较长链表,先走到与较短链表长度相同的位置,然后两个链表同时移动,如果有相交节点,则会有节点相等
if headA is None or headB is None:
return None
cur_a, cur_b = headA, headB
cur1, cur2 = headA, headB
count_a, count_b = 0, 0
# 统计两个链表的长度
while cur_a != None:
count_a += 1
cur_a = cur_a.next
while cur_b != None:
count_b += 1
cur_b = cur_b.next
# 判断两个链表的长度谁大
if count_a >= count_b:
for i in range(count_a - count_b):
cur1 = cur1.next # 长的链表,先走count_a - count_b个节点
while cur1 != cur2:
cur1 = cur1.next
cur2 = cur2.next
else:
for i in range(count_b - count_a):
cur2 = cur2.next # 长的链表,先走count_a - count_b个节点
while cur1 != cur2:
cur1 = cur1.next
cur2 = cur2.next
return cur1
四、LeetCode142.环形链表II
1:题目描述(142.环形链表II )
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
2:解题思路
使用双指针-快慢指针,快指针每次向后移动2个节点,慢指针每次向后移动1个节点,如果链表有环存在,则快指针会先进入环,然后在环中一直转圈,慢指针后进环,慢指针进入环后,会在慢指针进入环的第一圈与快指针相遇,此时使用一个指针index1,指向快慢指针相遇的节点,同时使用指针index2指向链表的头节点,然后同时移动index1和index2,每次移动一个节点,直到index1=index2,我们就找到了环的入口节点。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head):
# 使用双指针-快慢指针
# 快指针每次比慢指针多走一步,即快指针每次走两步,慢指针每次走一步
# 如果链表中存在有环的情况,则快指针则会比慢指针先入环
# 慢指针入环后,会在慢指针入环后的第一圈与快指针重叠
# 此时在定义两个指针,一个指向头节点,一个指向快慢指针相重叠的节点
# 两个指针同时移动,每次移动一个节点,会在环的入口处重叠,此时就找到了环的入口节点
fast, slow = head, head
while fast != None and fast.next != None:
fast = fast.next.next
slow = slow.next
if slow == fast:
index1 = slow
index2 = head
while index1 != index2:
index1 = index1.next
index2 = index2.next
return index1
return None
今日总结:
今日总体来说,思路还是比较清晰的,跟之前做过有关系,还能记得大概的思路,但是在写代码的过程中,有一些地方还是需要瞄一眼之前写的代码,才能写出正确的代码,不然的话,就会出现一些用例过不了的情况,或者然后就是卡壳了,感觉这也不对,那也不对,不知道要怎样才能把自己的想法用代码写出来。
这次的链表训练,让我理解了一些之前刷题没有理解的东西。
训练营打卡第四天,在周天完成了第四天的任务,稍微脱了一点进度。