Day04 代码随想录刷题
知识点:链表、快慢指针、链表有环问题
一、LeetCode24. 两两交换链表中的节点
1.解题思路
考点为虚拟头结点和链表指针的使用
2.代码实现
2.1 方法一:迭代法
从虚拟头结点开始往后循环判断是否存在2个元素,如果存在则进行两两交换并接入链表
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
# 迭代法
yhead = ListNode(-1,head)
pre = yhead
# 循环条件:节点后续是否存在2个元素
while pre.next and pre.next.next:
cur = pre.next
nxt = pre.next.next
# 开始交换
pre.next = nxt
cur.next = nxt.next
nxt.next = cur
# 更新循环条件
pre = cur
return yhead.next
指针pre
遍历了一遍整个链表,所以时间复杂度为O(n)
,空间复杂度 O(1)
。
2.2 方法二:递归法
确定递归中不变的逻辑:判断即将在链表尾部新接入的链表是否存在2个元素,即是否head
和head.next
为真:
若其中一个为假,则即将接入的链表元素少于2,不需要进行两两交换,直接接入即可
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
# 递归法
# 判断新接入链表元素是否大于2
if not head or not head.next:
return head
# 条件满足,开始两两交换并接入
new_head = head.next
head.next = self.swapPairs(new_head.next)
new_head.next = head
return new_head
递归法的时间复杂度为O(n)
,空间复杂度是 O(n)
二、LeetCode19.删除链表的倒数第N个节点
1.解题思路
快慢指针,快指针fast
先行N
步,然后一起前进直到fast.next
为空时,慢指针slow
的位置就是要删除节点位置的前一个节点
2.代码实现
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
yhead = ListNode(-1,head)
slow = fast =yhead
# 快指针先行n步
for _ in range(n):
fast = fast.next
# 一起前进
while fast.next:
slow = slow.next
fast = fast.next
# 删除操作
slow.next = slow.next.next
return yhead.next
时间复杂度为O(n)
,空间复杂度 O(1)
。
三、LeetCode面试题02.07. 链表相交
1.解题思路
考点为双指针,两个指针phead1
和phead2
各自从头结点headA
和headB
出发,走完本链表走另一条链表,相等时退出循环,退出后判断指针所指的值,因为路径长度相同,若:
值相等且存在,指针所指即链表相交的节点
值相等且为空,则两条链表没有相交的节点
2.代码实现
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
phead1 = headA
phead2 = headB
while phead1 != phead2:
phead1 = phead1.next if phead1 else headB
phead2 = phead2.next if phead2 else headA
# 循环结束
return phead1
时间复杂度为O(n)
,空间复杂度 O(1)
。
四、LeetCode142.环形链表II
1.解题思路
见下文
2.代码实现
2.1方法一:快慢指针法
快指针fast
每次走2步,slow
慢指针每次走1步,两者同时走,当第一次相遇时,快指针fast
回到起点,每次走一步,两者继续前进,再次相遇时指针所指的节点就是入环节点
为了避免链表无环,快慢指针前进的循环条件为fast
和fast.next
都存在,因为快指针是一次走2步,需要往前多判断一个next,以免出现对空指针求next的操作。
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
# 快慢双指针法
slow = fast = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast == slow:
# fast指针回到起点
fast = head
while slow != fast:
fast = fast.next
slow = slow.next
return slow
return None
时间复杂度为O(n)
,空间复杂度 O(1)
。
2.2方法二:集合法
利用数据容器集合set()内元素不会重复的特点,先判断链表节点是否在容器内存在:
若不存在,则将该节点加入容器
visited.add(head)
若存在,则返回该节点,因为该节点就是链表入环节点
每次操作完后,链表节点后移操作
head = head.next
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
visited = set()
while head:
if head in visited:
return head
visited.add(head)
head = head.next
return None
时间复杂度为O(n)
,该解法无论是否存在环,集合中都会存储 n 个节点,其中 n 是链表的长度。因此空间复杂度为O(n)
。