目录
24. 两两交换链表中的节点
题目链接:24. 两两交换链表中的节点 - 力扣(LeetCode)
文章讲解:代码随想录
解题卡点:没有理解链表的思想,如何处理奇数链表
①如何交换链表元素
按下图三个步骤所示进行节点的交换
②循环何时退出
因为是每两个节点进行交换,所以要同时保证这两个节点都不为空。若链表为偶数,则全部两两交换,若链表为奇数,最后一个节点的next为空,跳出循环,即这个节点不用交换。另外,在两两交换的情况下,cur指针每次后移两个节点。
# 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: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(None, head)
cur = dummy
while cur.next and cur.next.next:
temp = cur.next # 防止待交换的节点被修改
temp2 = cur.next.next.next
cur.next = cur.next.next # 步骤1
cur.next.next = temp # 步骤2
cur.next.next.next = temp2 # 步骤3
cur = cur.next.next # 移动指针
return dummy.next
# 时间复杂度 O(n)
# 空间复杂度 O(1)
19. 删除链表的倒数第N个结点
题目链接:19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
文章讲解:代码随想录
解题卡点:如何一遍找到倒数第N个结点
双指针法。让slow和fast同时指向虚拟头结点,要删除倒数第N个结点,则让fast先走N步,随后同时让slow和fast移动。当fast指向最后一个结点时,slow对应的就是倒数第N个结点。另外,链表中要删除某一个结点,指针需位于该结点的前一个结点。
清楚思路了代码就好写。
# 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: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy = ListNode(0, head)
slow = dummy
fast = dummy
for _ in range(n): # fast结点先走N步
fast = fast.next
while fast.next: # slow和fast同时移动,直至fast位于链表最后一个元素
slow = slow.next
fast = fast.next
slow.next = slow.next.next
return dummy.next
# 时间复杂度 O(n)
# 空间复杂度 O(1)
160. 相交链表
题目链接:面试题 02.07. 链表相交 - 力扣(LeetCode)
文章讲解:代码随想录
解题卡点:看不懂题
注意:不是判断curA.val == curB.val,而是判断curA == curB,即链表交点的指针要相等。这里的相等也包含了地址相等,具体是哪个交点由题目设定,对于解题来说只需要按思路把每个点判断一遍即可。
解题思路:
①定义两个指针,位于两个链表的头节点;
②遍历两个指针,求出两个链表的长度,计算长度差n;
③让更长的链表头指针先走n个单位;
④同时让两个指针向后移动,直至两指针相同,返回交点;
⑤遍历结束也没有交点返回None。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
lenA = self._get_length(headA)
lenB = self._get_length(headB)
if lenA > lenB: # 步骤3
headA = self._move_forward(headA, lenA-lenB)
else:
headB = self._move_forward(headB, lenB-lenA)
curA = headA
curB = headB
while curA and curB: # 步骤4
if curA == curB:
return curA
curA = curA.next
curB = curB.next
return None
def _get_length(self, head: ListNode): # 计算链表的长度
length = 0
cur = head
while cur:
length += 1
cur = cur.next
return length
def _move_forward(self, head: ListNode, step: int): # 将链表头指针向后移动
cur = head
while step:
cur = cur.next
step -= 1
return cur
# 时间复杂度 O(n)
# 空间复杂度 O(1)
142. 环形链表II
题目链接:142. 环形链表 II - 力扣(LeetCode)
文章讲解:代码随想录
解题卡点:没思路
①判断链表是否有环
就好比跑步,操场是个环,跑的快的一方迟早会追上跑圈慢的一方,也就是迟早会相遇。
双指针法,快慢指针。每当慢指针前进一步,快指针前进两步。如果快指针遇到None,说明快指针到链表末尾,无环;如果快指针遇到慢指针,说明存在环。根据相对运动,快指针每次比慢指针多走一步,即在环中快指针每次都会接近慢指针一步,所以当存在环的时候,他们一定会相遇。
②如何找到环的入口
推导见代码随想录。在相遇节点处定义一个指针index1,在链表头结点处定一个指针index2,让index1和index2同时移动,每次移动一个节点, 那么1、2相遇的地方就是环形入口的节点。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
slow = head
fast = head
while fast and fast.next: # 当fast和next为None,说明fast到了链表末尾,无环
slow = slow.next
fast = fast.next.next
if slow == fast: # 当环存在,slow和fast一定相遇
slow = head # 此时slow为index2,fast为index1
while slow != fast:
slow = slow.next
fast = fast.next
return slow
return None
# 时间复杂度 O(n)
# 空间复杂度 O(1)