双指针法,分为左右指针和快慢指针两种。其中左右指针在数组中运用较多,可以和滑窗法一起进行汇总:滑窗法运用
而快慢指针一般在链表中运用较多,在反转链表和定位链表节点及链表成环等逻辑中运用比较广泛。
141. 环形链表
逻辑非常简单,只要是环形的链表,那么快慢指针早晚会遇到。
值得注意的一点是,用try…except…来进行异常判定
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
try:
slow = head
fast = head.next
while fast is not slow:
fast = fast.next.next
slow = slow.next
return True
except:
return False
142. 环形链表 II
在上题的基础上,找到环形链表中环的起点,借用一张leetcode上的图。简单来说,由于兔子走两步,乌龟走一步,因此两者相遇时兔子走的距离是乌龟的一倍。
2
d
i
s
t
a
n
c
e
(
t
o
r
t
o
i
s
e
)
=
d
i
s
t
a
n
c
e
(
h
a
r
e
)
2distance(tortoise) = distance(hare)
2distance(tortoise)=distance(hare)
2
(
F
+
a
)
=
F
+
a
+
b
+
a
2(F+a) =F+a+b+a
2(F+a)=F+a+b+a
F
=
b
F = b
F=b
class Solution(object):
def detectCycle(self, head):
fast, slow = head, head
while True:
if not (fast and fast.next): return
fast, slow = fast.next.next, slow.next
#因此当两者相遇时,即图中的first intersection点时退出while循环
if fast == slow: break
#快指针重新移动到起点,两者同样的速度相遇时即为环的起点
fast = head
while fast != slow:
fast, slow = fast.next, slow.next
return fast
92. 反转链表 II
在之前的反转链表基础上,只反转一部分链表。因此可以引入两个指针分别定位需要反转链表的头和尾。中间反转链表后,和链表定位的头和尾重新连接即可。
class Solution:
def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
if m == n:
return head
dum = ListNode(-1)
#注意这一步指定链表的next
dum.next = head
a,c = dum,dum
for i in range(m-1):
a = a.next
for i in range(n):
c = c.next
b = a.next
d = c.next
pre = b
cur = pre.next
while cur != d:
next = cur.next
cur.next = pre
pre = cur
cur = next
a.next = c
b.next = d
return dum.next
另外除了双指针法之外,局部反转链表有一种递归的方式求解,也是在全局反转链表基础上的变幻,面试中如果能写出来,一定能加分不少。
class Solution(object):
def reverseBetween(self, head, m, n):
"""
:type head: ListNode
:type m: int
:type n: int
:rtype: ListNode
"""
#此函数的意义是反转链表的前n个节点
def reverseN(head,n):
if n == 1:return head
last = reverseN(head.next,n-1)
successor = head.next.next
head.next.next = head
head.next = successor
return last
if m == 1:return reverseN(head,n)
#递归调用直到m-1等于1
head.next = self.reverseBetween(head.next,m-1,n-1)
return head
234. 回文链表
因为是回文链表,所以只需要反转前半部分节点并和后一半链表进行对比即可
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
rev = None
fast = slow = head
while fast and fast.next:
fast = fast.next.next
rev,rev.next,slow = slow,rev,slow.next
if fast:
slow = slow.next
while rev and rev.val == slow.val:
rev = rev.next
slow = slow.next
return not rev
283. 移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
要求时间复杂度和空间复杂度尽可能低
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
l,r = 0,0
n = len(nums)
while r < n:
if nums[r] != 0:
nums[l],nums[r] = nums[r],nums[l]
l += 1
r += 1
return nums