一 单链表中的双指针
快慢指针找环的问题
如果存在环,快指针总能追上慢指针。
什么速度合适?其他速度怎么样?
例子:
Linked List Cycle
Given a linked list, determine if it has a cycle in it.
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
if head is None or head.next is None:
return False
slow = head
fast = head.next
# 比较安全的速度是fast是slow的两倍,也就是说每次fast比slow多跑一格。
# 如果fast过快,而且环很小的话,fast每次恰好跨过一个环,和slow永远无法相遇
# 如果fast过快,需要判断的条件过长 fast.next fast.next.next fast.next.next...
while fast.next and fast.next.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
Linked List Cycle II
Given a linked list, return the node where the cycle begins. If there is no cycle, return null
.
比较简单的做法,用hash表记录访问过的位置。空间是O(n)的。快慢指针的方法空间是O(1)。
快慢指针相遇的点是不确定的,但是快指针比慢指针多走过的距离是确定,恰好等于慢指针到环入口的位置。
用三个阶段,很容易解释:
设起点到环入口点的距离为x, 环的的长度为c,
一, 当slow指针到达入口点的时候,t = x次, fast移动的距离为 2 * x,相当于fast不仅到达了环而且已经在环中走了x的距离,此时fast距离slow的距离为 c - x
二 在环中的追击问题,fast 距离 slow 为 c - x,又因为fast的相对速度为1, 所以 移动 t = c - x次后, slow和fast相遇。当slow和fast相遇,此时slow 走过了 t * 1 = c - x 的距离, 那么相遇点距离 入口的距离就是 c - (c - x) = x
三 得到了距离关系,从起点到入口点距离为x, 从相遇点到入口点的距离也是x,所以两个点同时同速移动就一定会在入口点
相遇。
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
cur = head
visited = set()
while cur:
if cur in visited:
return cur
else:
visited.add(cur)
cur = cur.next
return None
# if head is None or head.next is None:
# return None
# slow = head
# fast = head
# hascycle = False
# while fast.next and fast.next.next:
# fast = fast.next.next
# slow = slow.next
# if slow == fast:
# hascycle = True
# break
# if not hascycle:
# return None
# slow = head
# while slow != fast:
# slow = slow.next
# fast = fast.next
# return slow
Intersection of Two Linked Lists
Write a program to find the node at which the intersection of two singly linked lists begins.
class Solution(object):
# def getIntersectionNode(self, headA, headB):
# """
# :type head1, head1: ListNode
# :rtype: ListNode
# """
# visited = set()
# while headA:
# if headA not in visited:
# visited.add(headA)
# headA = headA.next
# while headB:
# if headB in visited:
# return headB
# else:
# headB = headB.next
# def getIntersectionNode(self, headA, headB):
# if headA is None or headB is None:
# return None
# pa = headA # 2 pointers
# pb = headB
# while pa is not pb:
# # 找到两条链的长度差:短的到了末尾就 回到长的头。 在移动个 长度差,此时就能站到同一起跑线
# pa = headB if pa is None else pa.next
# pb = headA if pb is None else pb.next
# return pa
# 拼接两条链表,会形成一个环,然后找环即可
def getIntersectionNode(self, A, B):
if not A or not B: return None
# Concatenate A and B
last = A
while last.next: last = last.next
last.next = B
# Find the start of the loop
fast = slow = A
while fast and fast.next:
slow, fast = slow.next, fast.next.next
if slow == fast:
fast = A
while fast != slow:
slow, fast = slow.next, fast.next
last.next = None
return slow
# No loop found
last.next = None
return None
Remove Nth Node From End of List
Given a linked list, remove the n-th node from the end of list and return its head.
class Solution(object):
def removeNthFromEnd(self, head, n):
"""
:type head: ListNode
:type n: int
:rtype: ListNode
"""
curr = head
tail = head
for i in range(n):
tail = tail.next
if tail is None:
return head.next
while tail.next:
curr = curr.next
tail = tail.next
curr.next = curr.next.next
return head
快慢指针的痛点:
只需要判断fast是不是空即可(fast比slow快),
如果要判断fast.next 就必须是fast and fast.next(会报None没有next的错误)
总结:
快慢指针基本上都是用来找差值的,然后再去利用这差值去做事情。
二 单链表中的经典问题
Reverse Linked List
is the foundation of many linked-list problems
class Solution(object):
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
prev = None
while head:
curr = head
head = head.next
curr.next = prev
prev = curr
return prev
Remove Linked List Elements
Remove all elements from a linked list of integers that have value val.
要点:为了不对head做特殊处理,使用dummy head指向head
class Solution(object):
def removeElements(self, head, val):
"""
:type head: ListNode
:type val: int
:rtype: ListNode
"""
dummy = ListNode(-1)
dummy.next = head
curr = dummy
while curr.next:
if curr.next.val == val:
curr.next = curr.next.next
else:
curr = curr.next
return dummy.next
Odd Even Linked List
Given a singly linked list, group all odd nodes together followed by the even nodes.
要点:时刻关注链表的结构变化,使用了一个dummy去保存head.next,因为在while循环后, head和原先的head.next断开
class Solution(object):
def oddEvenList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head:
return head
odd = head
even = head.next
dummy = head.next
while even and even.next:
odd.next = odd.next.next
odd = odd.next
even.next = even.next.next
even = even.next
odd.next = dummy
return head
Palindrome Linked List
Given a singly linked list, determine if it is a palindrome.
1. 先用快慢指针找到中点:
关于初始的slow和fast如何设置?slow和fast都设在head,中点偏左,fast在slow后一位,中点偏右。
2 反转slow后的链表,反转后的链表mid指向空
3 head => mid <= tail:遍历一遍,当left和righ指向空的时候,说明遍历完成。
class Solution(object):
def isPalindrome(self, head):
"""
:type head: ListNode
:rtype: bool
"""
# find midian
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# reverse the link from slow to end
prev = None
while slow:
curr = slow
slow = slow.next
curr.next = prev
prev = curr
# traversal the link node
left = head
right = prev
while left and right:
if left.val == right.val:
left = left.next
right = right.next
else:
return False
return True
三 双链表和单链表的比较
If you need to add or delete a node frequently, a linked list could be a good choice.
If you need to access an element by index often, an array might be a better choice than a linked list.
四 经典题目
Merge Two Sorted Lists
Merge two sorted linked lists and return it as a new list.
技巧:构造一个dummy head,用or拼接剩下的部分
class Solution(object):
def mergeTwoLists(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
cur1 = l1
cur2 = l2
head = ListNode(-1)
dummy = head
while cur1 and cur2:
if cur1.val < cur2.val:
head.next = cur1
cur1 = cur1.next
else:
head.next = cur2
cur2 = cur2.next
head = head.next
head.next = cur1 or cur2
# if cur1:
# head.next = cur1
# if cur2:
# head.next = cur2
return dummy.next
Add Two Numbers
You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
class Solution(object):
# def addTwoNumbers(self, l1, l2):
# """
# :type l1: ListNode
# :type l2: ListNode
# :rtype: ListNode
# """
# head1 = l1
# head2 = l2
# res = 0
# n = 0
# while head1 and head2:
# res += pow(10, n) * (head1.val + head2.val)
# n += 1
# head1 = head1.next
# head2 = head2.next
# head = head1 or head2
# while head:
# res += pow(10, n) * head.val
# n+= 1
# head = head.next
# if res == 0:
# return ListNode(0)
# out = ListNode(-1)
# dummy = out
# while res:
# res, mod = divmod(res, 10)
# out.next = ListNode(mod)
# out = out.next
# return dummy.next
def addTwoNumbers(self, l1, l2):
carry = 0
root = n = ListNode(0)
while l1 or l2 or carry:
v1 = v2 = 0
if l1:
v1 = l1.val
l1 = l1.next
if l2:
v2 = l2.val
l2 = l2.next
carry, val = divmod(v1+v2+carry, 10)
n.next = ListNode(val)
n = n.next
return root.next