24. 两两交换链表中的节点
迭代
- 先考虑交换的两个节点,看作一组,组内交换;再考虑组间如何连接,指针如何更新
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
# Time complexity: O(N)
# Space complexity: O(1)
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummy_head = ListNode(0,head)
prev = dummy_head
cur = head
while cur and cur.next:
next_node = cur.next
# swap node
cur.next = next_node.next
next_node.next = cur
prev.next = next_node
# update node
prev = cur
cur = cur.next
return dummy_head.next
# another way to indicate the pointer
# 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_head = ListNode(0,head)
# connect node always one node ahead of the swap nodes group
connect = dummy_head
while connect.next and connect.next.next:
swap_1 = connect.next
swap_2 = connect.next.next
# swap node
swap_1.next = swap_2.next
swap_2.next = swap_1
# connect to the second node in the swap nodes group (because of swapping, swap_2 is at the beginning of the nodes group)
connect.next = swap_2
# update node
connect = swap_1
return dummy_head.next
19. 删除链表的倒数第 N 个结点
利用栈
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
# Time complexity: O(N)
# Space complexity: O(N)
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
node_stack = []
dummy_head = ListNode(-1, head)
cur = dummy_head
while cur:
node_stack.append(cur)
cur = cur.next
prev, next_node = None, None
for i in range(n+1):
cur = node_stack.pop()
if i == n-2:
next_node = cur
elif i == n:
prev = cur
prev.next = next_node
return dummy_head.next
两次遍历
- 第一次遍历找到链表长度
- 第二次遍历找到删除节点
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
# Time complexity: O(N)
# Space complexity: O(1)
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy_head = ListNode(-1, head)
cur = dummy_head
length = 0
while cur:
cur = cur.next
length += 1
prev = dummy_head
for i in range(length - n - 1):
prev = prev.next
next_node = prev.next.next
prev.next = next_node
return dummy_head.next
一次遍历
- 快慢指针,慢指针在快指针出发遍历过n+1之后出发,当快指针到达链表尾时,慢指针指向的就是删除节点的前一个指针
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
# Time complexity: O(N)
# Space complexity: O(1)
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy_head = ListNode(-1, head)
slow, fast = dummy_head, dummy_head
while fast:
if n < 0:
slow = slow.next
fast = fast.next
n -= 1
slow.next = slow.next.next
return dummy_head.next
面试题 02.07. 链表相交
暴力解法:双循环(超时)
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
# Time complexity: O(M*N)
# Space complexity: O(1)
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
cur_A = headA
while cur_A:
cur_B = headB
while cur_B:
if cur_A == cur_B:
return cur_A
cur_B = cur_B.next
cur_A = cur_A.next
return None
利用Set(HashTable)来存储节点进行比较
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
# Time complexity: O(M+N)
# Space complexity: O(M)
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
cur_A = headA
node_set = set()
while cur_A:
node_set.add(cur_A)
cur_A = cur_A.next
cur_B = headB
while cur_B:
if cur_B in node_set:
return cur_B
cur_B = cur_B.next
return None
双指针
-
A,B两个链表可能存在交集的长度最长为min(len(A),len(B)),并且是从最后一个节点开始数起
-
所以假定len(B) > len(A),我们从链表A的头节点和链表B的第len(B)-len(A)个节点开始两两对应比较,如果能找到相同的第一个节点就返回它;不然就返回None
-
如何找到较长链表的比较起始节点?
- 利用双指针,当较短链表遍历完整个链表时,较长链表还剩下len(B)-len(A)个节点没有遍历,此时将原本遍历较短链表的指针指向较长链表的头节点,当较长链表遍历完成时,遍历较短链表的指针将指向较长链表比较的第一个节点
- 此时将遍历较长链表的指针指向较短链表的头节点,开始两两比较节点
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
# Time complexity: O(M+N)
# Space complexity: O(1)
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
pA = headA
pB = headB
while pA != pB:
pA = headB if pA is None else pA.next
pB = headA if pB is None else pB.next
return pA
142. 环形链表 II
快慢指针找环是否存在,双指针找链表尾连接到链表中的位置
-
快慢指针:快指针一次走两个节点,慢指针一次走一个节点,如果链表中有环,则快指针一定会在环中的某个节点追上慢指针(快指针比慢指针走得 快1个节点,即每次靠近慢指针一个节点,直到相遇)(此方法可以用于环的检测)
-
让双指针一个指向链表头,一个指向快慢指针找到的在环中的一个节点,开始遍历链表。当两个指针相遇时,此时的节点就是链表尾连接到链表中的位置
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
# Time complexity: O(N)
# Space complexity: O(1)
class Solution:
def get_intersect(self, head):
fast, slow = head, head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if slow == fast:
return slow
return None
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
if not head:
return None
intersect = self.get_intersect(head)
if not intersect:
return None
p_head = head
p_intersect = intersect
while p_head != p_intersect:
p_intersect = p_intersect.next
p_head = p_head.next
return p_head
HashTable存储遍历过的节点
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
# Time complexity: O(N)
# Space complexity: O(N)
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
node_set = set()
cur = head
while cur:
if cur in node_set:
return cur
node_set.add(cur)
cur = cur.next
return None
链表总结
链表经典题目解法
虚拟头节点dummy_head
使用虚拟头节点可以让对所有节点的操作一致
dummy_head = ListNode(0)
dummy_head.next = head
快慢指针
检测链表是否有环
fast = fast.next.next
slow = slow.next
双指针
可以用来找从链表末数起的第n个节点
while fast:
if n < 0:
slow -= slow.next
fast = fast.next
n -= 1