目录
两两交换链表中的结点
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
本题首先要找规律:不难看出其实就是两个元素为一组,进行交换,再指向下一组
不难看出,交换后,只需要cur = cur.next就可以对3、4重复步骤1、2、3。
❗注意我们返回的应该是交换后链表的头节点
# 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]:
if head == None or head.next == None:
return head
cur = ListNode(None,head)
p = head.next
while cur.next and cur.next.next:
temp1 = cur.next
cur.next = cur.next.next
temp2 = cur.next.next
cur.next.next = temp1
temp1.next = temp2
cur = temp1
return p
另外,这题也可以用递归
# 递归版本
# 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]:
if head is None or head.next is None:
return head
# 待翻转的两个node分别是pre和cur
pre = head
cur = head.next
next = head.next.next
cur.next = pre # 交换
pre.next = self.swapPairs(next) # 将以next为head的后续链表两两交换
return cur
删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第
n
个结点,并且返回链表的头结点。
本题比较简单,只需要先设一个虚拟头结点,然后进行dummy_head.next的循环length-n次。
快慢双指针
但是,如果要实现一趟扫描,就复杂一点。这里要引入快慢双指针:
双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
- 设置虚拟头节点可以方便处理删除头节点的问题
- fast移动n+1步→这样后面fast和slow同时移动时,两者始终间隔n→slow最终指向删除结点的前一个位置
- 注意区分链表末尾与链表最后一个元素:链表末尾为None;而链表最后一个(倒数第一个)元素是4
(下图n=2)
❗不能return head,因为在使用虚拟头节点删除头节点的方法中,我们是通过断开了dummy_head与head之间的链,而不是使head = head.next;如果return 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]:
dummy_head = ListNode(None,head)
fast,slow = dummy_head,dummy_head
for _ in range(n+1): #fast先走n+1步
fast = fast.next
while fast:
fast = fast.next #fast和slow同时走,直到fast到链尾
slow = slow.next
slow.next = slow.next.next #删除结点
return dummy_head.next
链表相交
给你两个单链表的头节点
headA
和headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回null
。图示两个链表在节点
c1
开始相交:题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
由图可以看出,如果A、B两个链表分开,其实应该是末端对齐,然后进行匹配的(因为他们一定从某一节点起到末尾是相同的)
❗遍历长度时用cur(=head)来遍历→如果用head来遍历,遍历结束后head指向链尾,链头指针被改变,无法再次遍历
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
lenA, lenB = 0, 0
cur = headA
while cur: # 求链表A的长度
cur = cur.next
lenA += 1
cur = headB
while cur: # 求链表B的长度
cur = cur.next
lenB += 1
curA, curB = headA, headB
if lenA > lenB: # 让curB为最长链表的头,lenB为其长度
curA, curB = curB, curA
lenA, lenB = lenB, lenA
for _ in range(lenB - lenA): # 让curA和curB在同一起点上(末尾位置对齐)
curB = curB.next
while curA: # 遍历curA 和 curB,遇到相同则直接返回
if curA == curB:
return curA
else:
curA = curA.next
curB = curB.next
return None
环形链表II
给定一个链表的头节点
head
,返回链表开始入环的第一个节点。 如果链表无环,则返回null
。如果链表中有某个节点,可以通过连续跟踪
next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果pos
是-1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。
快慢指针法-判断环
可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
在环中,对于slow来说,fast其实在一个一个结点追上他,所以他们必定会在环内相遇。 且相遇点一定在环的入口 (为什么相遇点一定在入口?)
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# If there is a cycle, the slow and fast pointers will eventually meet
if slow == fast:
# Move one of the pointers back to the start of the list
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
return slow
# If there is no cycle, return None
return None