24.两两交换链表中的节点
思路
需要3次交换,主要是搞清楚交换的顺序和保存。(图:代码随想录)
方法一:循环+虚拟头节点
·cur指向2(此前需要保存1为temp1)
·2指向1(temp1赋值给2,此前需要保存3为temp2)
·1指向3(temp2赋值给1)
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def swapPairs(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
# 循环+虚拟头节点版本
dummy_head = ListNode(0, head)
cur = dummy_head
while cur.next and cur.next.next: # 判断后两位都是可以交换的
# 先保存原来的顺秀,对应图上的2、3
temp1 = cur.next
temp2 = cur.next.next.next
# 嫁接cur到2
cur.next = cur.next.next
# 嫁接2到1
cur.next.next = temp1
# 嫁接1到3
cur.next.next.next = temp2
# 更新cur跨过2位
cur = cur.next.next
return dummy_head.next #返回虚拟节点的下一位(2)
方法二:递归
递归相当于把head向后传递,也就是next作为首节点依次翻转。
注意,最终return的是cur,是第一次交换的cur。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def swapPairs(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
# 递归
if head is None or head.next is None:
return head
pre = head
cur = head.next
next = head.next.next
cur.next = pre
pre.next = self.swapPairs(next)
return cur
19.删除链表的倒数第N个节点
思路
双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def removeNthFromEnd(self, head, n):
"""
:type head: ListNode
:type n: int
:rtype: ListNode
"""
dummy_head = ListNode(0, head)
fast = dummy_head
slow = dummy_head
# 先让 fast 领先slow n 个位置
for _ in range(n):
fast = fast.next
# 当fast走到末尾,slow指向的就是倒数第n个节点前一个节点
while fast.next:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dummy_head.next
面试题 02.07.链表相交
思路
注意!这里的相交不是值相等,而是指针相等,所以不能采取一一对应的遍历的方式,而嵌套for带来时间上开销太大。
链表长度+同时出发
假设A比B长,那么只需要将A和B对齐,再依次往后找指针相同的点即可。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
# 链表长度 + 同时出发
# Step 1:获取A、B长度
lenA, lenB = 0, 0
cur = headA
while cur:
cur = cur.next
lenA += 1
cur = headB
while cur:
cur = cur.next
lenB += 1
# Step 2:对齐curA、curB
curA, curB = headA, headB
if lenB > lenA: # 如果B的长度比A长,那么交换两个链表,始终保持A是长的那一个
curA, curB = curB, curA
lenA, lenB = lenB, lenA
for _ in range(lenA - lenB): # 只移动curA,实现对齐
curA = curA.next
# Step 3:寻找交叉点
while curA:
if curA == curB:
return curA
else:
curA = curA.next
curB = curB.next
return None
链表长度+同时出发(函数复用版)
将以上获取长度和对齐的步骤实现封装复用。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
# 链表长度 + 同时出发(复用函数型)
lenA, lenB = self.getlen(headA), self.getlen(headB)
curA, curB = self.align(headA, headB, lenA, lenB)
while curA and curB:
if curA == curB:
return curA
else:
curA = curA.next
curB = curB.next
return None
def getlen(self, head):
len = 0
while head:
head = head.next
len += 1
return len
def align(self, headA, headB, lenA, lenB):
if lenB > lenA:
headA, headB = headB, headA
lenA, lenB = lenB, lenA
for _ in range (lenA - lenB):
headA = headA.next
return headA, headB
循环自动对齐法(巧妙,值得借鉴)
while curA != curB:
curA = curA.next if curA else headB
curB = curB.next if curB else headA
return curA
首先我们假设lenA 比 lenB大,实际情况下都可以,这里方便描述:
Step 1:此时A、B都没有被穷尽,所以都执行next操作;
Step 2:此时B被穷尽了,curB跳转到A的开头,此时curA所在的位置超前curB正好是lenB个位置;
Step 3:接着A被穷尽了,curA跳转到B的开头,此时curB所在的位置是距离A的结尾lenB个位置的地方;而curA距离B的结尾也是lenB个位置,相当于实现了对齐。
完整代码如下,
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def getIntersectionNode(self, headA, headB):
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
# 循环自动对齐法
if not headA or not headB: # 边界情况:A或B有一个是空的
return None
curA, curB = headA, headB
while curA != curB:
curA = curA.next if curA else headB
curB = curB.next if curB else headA
return curA
142.环形链表II
思路
快慢指针+追及
可以简单描述为:快指针一次走2格,慢指针一次走1格,直到相遇。此时从头和相遇点分别按照一次走1格来遍历,直到相遇,相遇的点就是所求的环的入口。(图:代码随想录)
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
# 快慢指针
fast, slow = head, head
while fast and fast.next:
slow = slow.next # 慢指针一次走1个
fast = fast.next.next # 快指针一次走2个
if fast == slow: # 当2个指针相遇
slow = head # 从起始位置开始遍历,直到遇见
while fast != slow:
fast = fast.next
slow = slow.next
return slow # 相遇返回指针
return None # 没相遇返回null
集合法(描述简单,但内存大)
只需要判断现在的是不是以前出现过了就可以。为此设置一个集合专门用来盛放已经遇到的节点。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
# 集合法
visited = set() # 创建一个盛放已经遍历过的点的集合
cur = head
while cur:
if cur in visited: # 见到过直接返回cur
return cur
visited.add(cur) # 没见过的就加进来
cur = cur.next
return None
第四天完结🎉