前一天学习了链表的数据结构 ,题目都是使用的单链结构, 今天接触了一下更复杂的链表结构。
24. Swap Nodes in Pairs (Medium)
思路: 交换第 i, i+1 个节点需要 1. 第 i 个节点的指针指向 i+2,2, i + 1 指针指向 i 3. i - 1 指向 i + 1。 4. 当前处理的节点从 i 移到 i + 2, 前一个节点从 i - 1 移动到交换之后的 i。
难点: 注意由于需要处理 i - 1. 可以在head 前面再添加一个节点。 注意while loop 跳出条件。
# 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]:
new_head = ListNode(0, head)
prev_node = new_head
while head and head.next:
next_node = head.next
tmp_node = next_node.next
head.next = tmp_node
next_node.next = head
prev_node.next = next_node
# update position
prev_node = head
head = tmp_node
return new_head.next
19. Remove Nth Node From End of List
思路:最容易想到的是先计算链表的长度 l, 然后删除第 l - n + 1 个节点。 题目提示可以用一次循环, 那么可以用到双指针, 先让快指针走n 步, 然后快慢指针同步往前走知道快指针走到头儿,慢指针指向的下一个节点就是要删除的节点。 有点像slicing window 那样操作。
难点:head 前增加新的 node 来一般化删除head 的特殊情况。
# 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]:
new_head = ListNode(0, head)
fast_node = new_head
slow_node = new_head
i = 0
while fast_node and i<n:
fast_node = fast_node.next
i += 1
while fast_node.next:
slow_node = slow_node.next
fast_node = fast_node.next
# remove slow_node.next
slow_node.next = slow_node.next.next
return new_head.next
160. Intersection of Two Linked Lists
思路: 其实和上一个问题类似, A, B 两个链表的指针从 head 往后移动, 直到一个到头。 另一个如果还没有到头, 那说明非相交的部分不一样长。 不是一般性的假设 A 走完了,B 没有走完, 那么 B 的指针和head 同时移动知道 B 到头儿, 这样A 和新的 B 就有一样的长度。 然后 A,B 的head 同时往后走,如果能碰到, 就是交汇点, 如果碰不到, 就是没有交汇点。
难点: 无
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
tmp_nodeA, tmp_nodeB = headA, headB
while tmp_nodeA and tmp_nodeB:
tmp_nodeA = tmp_nodeA.next
tmp_nodeB = tmp_nodeB.next
# if tmp_nodeA is not None, length of LinkedList A is greater than B
# move head of A forword until tmp_nodeA to None to make A and B have the same length
while tmp_nodeA:
headA = headA.next
tmp_nodeA = tmp_nodeA.next
while tmp_nodeB:
headB = headB.next
tmp_nodeB = tmp_nodeB.next
# move headA and head B with same speed until they meet each other.
while headA and headB:
if headA == headB:
return headA
headA = headA.next
headB = headB.next
return None
142. Linked List Cycle II
思路:之前做过使用memory O(1) 算法判断链表有无循环,直接套圈就行。 但是找到节点难度要高上不少。 需要一些 math 的推算:
假设环的节点有 j个。
快指针走两步, 慢指针走一步, 设快针走x步遇到第一次慢针。
当 x % 2 = 0 时,快针走了x, 慢针走了x/2, 快针比慢针多走了 n * j, n为正整数。
,
此时慢针退回原点,快慢针同时同速度走, 可以满足快慢针再次相遇, 并且正好在入口。
同理,当 x % 2 = 1 时
,
此时慢针退回原点,快针前走一步, 然后快慢针同时同速度走, 可以满足快慢针再次相遇, 并且正好在入口。
难点: 思路就是难点, 实现比较简单
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
slow_node, fast_node = head, head
i = 0
# first check if there is a loop
while fast_node:
i += 1
fast_node = fast_node.next
if i % 2 == 0:
slow_node = slow_node.next
if slow_node == fast_node: break # there is a loop
if not fast_node: return None
# find loop start node
slow_node = head
if i % 2 == 1: fast_node = fast_node.next
while True:
if fast_node == slow_node: return fast_node
fast_node = fast_node.next
slow_node = slow_node.next