代码随想录算法训练营第四天(链表篇)|Leetcode24两两交换链表节点,Leetcode19删除倒数第n个节点,链表相交,环形链表
Leetcode 24 两两交换链表节点
题目链接: 两两交换链表节点
给定一个链表,要求两两交换其节点,最后返回其处理完成后的头节点。
如1->3->2->4->5
,处理后得到3->1->4->2->5
思路:
本题重点还是明确链表操作过程中,每一步做了什么操作。详情见下图。
在操作链表节点的场景中,只需要明确每一步完成了什么操作,注意及时保存更新节点即可。
此处引入虚拟头节点是为了方便处理实际头节点。
具体代码实现
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummyHead = ListNode(0, head)
cur = dummyHead
while cur.next and cur.next.next:
prevNode, nextNode = cur.next, cur.next.next
temp = nextNode.next
nextNode.next = prevNode
prevNode.next = temp
cur.next = nextNode
cur = prevNode
return dummyHead.next
时间复杂度: O(n)
Leetcode 19 删除倒数第n个节点
题目链接: 删除倒数第n个节点
给定一个链表,要求删除其倒数第n个节点。
如1->2->3->5->4
,删除倒数第2个节点后得到1->2->3->4
思路:
在单向链表中,若希望删除一个节点,则首先需要找出其前一个节点,随后修改其前一个节点指针的指向以完成删除操作(视语言的不同需要手动释放内存)。此处要删掉倒数第n个节点,自然需要找出倒数第n个节点的前一个节点。为此,我们可以采用双指针法。让快指针领先慢指针n个单位,这样当快指针移动到链表末尾时,慢指针正好移动到倒数第n个节点的前一个节点。
具体代码实现
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummyHead = ListNode(0, head)
slow, fast = dummyHead, dummyHead
while n:
fast = fast.next
n-=1
while fast.next:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dummyHead.next
时间复杂度: O(n)
链表相交
题目链接: 链表相交
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
思路: 若希望判断两个单链表相交,最直观的方式为,在遍历两个链表时,同时遍历到同一个节点,则能够说明两个链表存在交点。而给出的两个单链表的长度不一致,因此,我们自然想到需要转化问题: 若两个单链表长度一致,同时从头遍历,就一定能够找出相交的起始节点。我们先统计两个单链表的长度,在截长后同时遍历即可。
具体代码实现:
class ListNode:
def __init__(self, val=0, next=None) -> None:
self.val = val
self.next = next
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
curA, curB = headA, headB
lengthA, lengthB = self.getLength(curA), self.getLength(curB)
diff = abs(lengthA - lengthB)
if lengthA < lengthB:
headA, headB = headB, headA
for _ in range(diff):
headA = headA.next
while headA and headB:
if headA == headB:
return headA
headA = headA.next
headB = headB.next
return None
def getLength(self, head: ListNode) -> int:
length = 0
while head:
head = head.next
length += 1
return length
时间复杂度: O(n)
Leetcode 142 环形链表
题目链接: 环形链表
给定一个单链表,判断链表中是否成环,若成环则返回环的入口节点。
思路: 本题需要综合结合链表性质和数学知识求解。
首先,我们需要判断链表是否成环;若成环,我们需要找出入口节点。
对于第一个问题,我们可以通过快慢指针的方式,判断是否成环。类似操场跑步时的“套圈”,在环形跑道中,快速跑者一定会与慢速跑者相遇。同理,遍历链表,若快慢指针最终相遇,则说明存在环;否则不存在环。
对于第二个问题,我们不妨画图列方程辅助理解:
假设从链表入口位置到环入口节点位置之间的举例为n
,慢指针在快指针在环内转了t
圈后,与快指针相遇,从环入口节点到快慢指针相遇的位置距离为x
,从相遇位置出发回到环入口节点距离为y
。快指针速度为慢指针的2倍。
则有: n+t(x+y)+x = 2(n+x)
,即(t-1)x+ty = n
,令t=1
,则有y=n
。即两个指针相遇的位置和链表起始位置,到环起始处的距离相同。
具体代码实现
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
slow, fast = head, head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast == slow:
break
if not fast or not fast.next: # 快指针为空时,链表中不存在环
return
while head != slow:
head = head.next
slow = slow.next
return slow