双指针技巧:左右指针和快慢指针
左右指针:两个指针相向而行或相背而行
快慢指针:两个指针同向而行,一快一慢
1、合并两个有序链表
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def mergeTwoList(self, list1, list2):
# 构建虚拟头节点dummy
dummy = ListNode(-1)
p = dummy
p1 = list1
p2 = list2
while p1 and p2:
if p1.val > p2.val:
p.next = p2
p2 = p2.next
else:
p.next = p1
p1 = p1.next
# 要注意p指针要不断前进
p = p.next
if p1:
p.next = p1
if p2:
p.next = p2
return dummy.next
需要构建虚拟头节点的情况:
当需要创造一条新链表的时候,可以使用虚拟头节点简化边界情况的处理。
2、分割链表
给定一个 链表的头节点和一个特定值x,对链表进行分割,使所有小于x的节点都出现在大于或等于x的节点之前。
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def SegmTwoList(self, head: ListNode, x: int)->ListNode:
dummy1, dummy2 = ListNode(-1), ListNode(-1)
# 用两个指针指向虚拟头节点,生成结果链表
p1, p2 = dummy1, dummy2
p = head
while p:
if p.val < x:
p1.next = p
p1 = p1.next
else:
p2.next = p
p2 = p2.next
# 断开原链表中每个节点的next指针
# 也就是断开节点之间的连接
temp = p.next
p.next = None
p = temp
# 连接两个链表,dummy2是完整的链表,p2只有一个 节点
p1.next = dummy2.next
return dummy1.next
3、合并k个有序链表
两两合并,分而治之
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def mergeKList(self, lists:List[Optional[ListNode]])->Optional[ListNode]:
if not len(lists): return None
return self.merge(lists, 0, len(lists)-1)
def merge(self, lists, l, r):
if l == r: return lists[l]
m = l+(r-1)//2
return self.mergeTwoLists(self.merge(lists, l, m), self.merge(lists, m+1, r)
def mergeTwoLists(self, list1, list2):
dummy = ListNode(-1)
p = dummy
p1, p2 = list1, list2
while p1 and p2:
if p1.val > p2.val:
p.next = p2
p2 = p2.next
else:
p.next = p1
p1 = p1.next
p = p.next
if p1:
p.next = p1
if p2:
p.next = p2
return dummy.next
4、单链表的倒数第k个节点
设置两个指针,一个指针先走k步,然后再同时向前走,等第一个指针走完,第二个指针所指的就是倒数第k个节点
def findK(head, k):
p1 = head
for i in range(k):
p1 = p1.next
p2 = head
while p1:
p1 = p1.next
p2 = p2.next
return p2
5、单链表的中点
快慢指针,一个走两步,一个走一步,两步的走到底,那一步的指针指向的就是单链表中点
def middleNode(head):
p1, p2 = head, head
while p1.next and p1:
p1 = p1.next.next
p2 = p2.next
return p2
6、判断链表是否有环
也是利用快慢指针,一个走两步,一个走一步,若两个指针相遇,则说明有环,若快指针为空,则说明无环
def isCycle(head):
p1, p2 = head, head
while p1.next and p1:
p1 = p1.next.next
p2 = p2.next
if p1 == p2:
return True
return False
若有环,返回链表开始如环的第一个节点,若无环返回空
若有环,那么假设慢指针走了k步,则快指针走了2k步,若假设相遇点距环起点为m,则头节点距环起点为k-m步,相遇点也距环起点k-m步。也就是说,从头开始与从相遇点开始走到两个指针相遇的时候就是环起点了。
def detCycle(head):
p1, p2 = head, head
while p1.next and p1:
p1 = p1.next.next
p2 = p2.next
if p1 == p2:
break
if not p1 or not p1.next:
return None
p1 = head
while p1 != p2:
p1 = p1.next
p2 = p2.next
return p1
7、两个链表是否相交
def getIntersection(headA, headB):
p1, p2 = headA, headB
while p1 != p2:
if not p1:
p1 = headB
else:
p1 = p1.next
if not p2:
p2= headA
else:
p2 = p2.next
return p1
循环条件是p1!=p2,若不相交,那么经过添加不同链表到结尾,遍历到最后也会在空节点相交,也就是返回的值为空。