链表两两交换
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
--------------------------------------------------------------------
因为涉及到链表的交换操作,头结点的交换与其他节点交换有不一样
因此添加一个虚拟头结点简化操作。
两两交换需要两个两个指针,一个指针没法操作,自己画图就能看到(如下)
我在这里想到有两个 一个prev指针,一个cur指针,分别初始化为dummy_head和head.
那么我们如何交换呢,需要保存哪些信息呢
首先两两交换,原来的prev.next == head,但是要指向cur.next,即prev.next = cur.next
那么就完成了第一步(图中1),
接下来我们需要保存cur要指向的指针,设next=cur.next,即cur应该指向cur.next.next 即cur.next = next.next.这就是第二步
然后还有就是next.next应该指向cur了,那就是next.next = cur
最后,变换的区间要移动了, prev = cur, cur = next.next 。具体步骤可以看图
最后一个细节,就是循环条件,其实就是cur和cur.next存在,不然就无法就进行两两交换的操作
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummy_head = ListNode(next=head)
prev = dummy_head
cur = dummy_head.next
while (cur and cur.next):
prev.next = cur.next
temp = cur.next.next
cur.next.next = cur
cur.next = temp
prev = cur
cur = temp
return dummy_head.next
删除链表倒数第N个节点
常规思路
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
这题的核心是倒数第N个,如何实现倒数第N个。
第一个最常规的思路就是我先遍历一遍整个链表,得到整个链表的长度T,
然后T-n就可以定位到它的前一个节点,然后做删除操作。
双指针思路
我们设置一个快指针,fast,让它先走N+1步(设置N+1的目的,删除列表要在它删除的前一个节点操作),设置一个慢指针,从头开始操作。两个指针的移动速度都是1个指针,当fast走到底后,slow此时的位置就是到第N+1指针了。此时就可以操作了
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy_head = ListNode(next=head)
fast = dummy_head
slow = dummy_head
# 先走N+1
for i in range(n+1):
fast = fast.next
while fast:
slow = slow.next
fast = fast.next
slow.next = slow.next.next
return dummy_head.next
链表相交
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
这个题目也没有什么技巧,其实思想很朴素。我们先要遍历知道两个链表的长度,计算两者长度的差值,然后让长的链表移动这个差值。此时现在指针指向的两个链表长度一致,就可以遍历查看是否有相等的节点。如果有,那么相交,反之则无相交的点。
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
cur1 = headA
cur2 = headB
len1 = 0
len2 = 0
while cur1:
len1 += 1
cur1 = cur1.next
while cur2:
len2 += 1
cur2 = cur2.next
gap = abs(len1-len2)
cur1 = headA
cur2 = headB
if len1 > len2:
for i in range(gap):
cur1 = cur1.next
else:
for i in range(gap):
cur2 = cur.next
while cur1:
if cur1 == cur2:
return cur1
cur1 = cur1.next
cur2 = cur2.next
return None
环形链表
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
这个很让人摸不着头脑。其实可以分解成两个问题
第一个是如何判断有环
第二个是找到环的入口点
对于第一个问题,使用的是快慢指针的写法。设定两个指针,起点都是head, 快指针,每次走两步,慢指针每次走一步,快指针每次都比慢指针多走一步。如果有环,必然能遇到。
对于第二个问题,具体可以参考代码随想录的讲解,讲解非常细致。核心就是,当快慢指针相遇时,我们标记此时的指针,并初始化一个指针,起点为head. 他们同时移动,移动步数为1,相遇时即为入口点。
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast = head
slow = head
while (fast and fast.next):
fast = fast.next.next
slow = slow.next
if slow == fast: # 说明有环
slow = head
# 找入口 初始化节点为head
while (slow):
slow = slow.next
fast = fast.next
return slow
return None