1、现在再看链表问题,发现之前对傻傻分不清的cur.next 有了更清晰的理解了:以cur变量表示当前节点,cur.next 为当前节点的指针 或下个节点 ,以上两个含义是一个意思,但在实际代码里选择其中一个进行理解会在逻辑上更清晰
2、cur.next != None 出现在if或while的条件位置 时,按照下个节点 理解
3、cur.next = xxxxx ,即cur.next放在等号左侧 时,按照给当前节点的指针 赋值理解
4、xxxxx = cur.next ,即cur.next放在等号右侧 时,按照指向或操作下个节点 理解
5、本文针对的总结题型为采用双指针来解决
206. 反转链表
双指针之前后指针 ,改变后指针指向前指针,关键点是 后指针指向要提前预存,要把None也当作一个特殊的节点。
python
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def reversedList(self, head: ListNode) -> ListNode:
# 情况1、链表为空
if head == None:
return None
# 情况2、链表非空
pre, cur = None, head # 没想到这样给初始值
while cur != None:
post = cur.next # 存储下个节点
cur.next = pre # 反转
pre, cur = cur, post # 更新两个指针
return pre
if __name__ == "__main__":
obj = Solution()
while True:
try:
in_line = input().strip().split('[')[-1].split(']')[0]
nums = []
head = None
if in_line == '':
print([])
else:
nums = [int(n) for n in in_line.split(',')]
head = ListNode(nums[0], None) # 创建链表头节点
cur = head
for n in nums[1:]: # 添加链表元素
cur.next = ListNode(n, None)
cur = cur.next
# 打印链表: 不用虚拟头节点也可以很方便地进行遍历打印
cur = head
while cur != None:
print(cur.val, end=' ')
cur = cur.next
obj.reversedList(head)
except EOFError:
break
19. 删除链表的倒数第 N 个结点
双指针之快慢指针 ,假设链表一共m个节点,倒数第n个节点 等价于 第m-n+1个节点 ;要注意按照题意 链表的节点索引从1开始。
python
from typing import Optional
'''
19. 删除链表的倒数第 N 个结点
题目描述:给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
题眼:假设链表一共m个节点,倒数第n个节点 等价于 第m-n+1个节点
注意:链表的节点索引从1开始
思路:
'''
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]:
# 注意链表的节点索引从1开始
# 假设链表一共m个节点,倒数第n个节点 等价于 第m-n+1个节点
# 删除第m-n+1个节点 需要找到 第m-n个节点
dummyHead = ListNode() # 创建虚拟头节点,使得对头节点的操作与其它节点一致
dummyHead.next = head
slow, fast = 0, 0 # 双指针之快慢指针,索引用来标记是第几个节点,减少代码错误
slowCur, fastCur = dummyHead, dummyHead
while fastCur != None:
if fast > n: # 当快指针移动到第n个节点时,慢指针同步移动,最终遍历完链表时,慢指针刚好移动m-n步,指向 第m-n个节点
slow += 1
slowCur = slowCur.next
fast += 1
fastCur = fastCur.next
slowCur.next = slowCur.next.next
return dummyHead.next
if __name__ == "__main__":
obj = Solution()
while True:
try:
in_line = input().strip().split('=')
nums = [int(n) for n in in_line[1].split('[')[1].split(']')[0].split(',')]
n = int(in_line[2].strip())
head = ListNode(nums[0])
cur = head
for i in range(1, len(nums)):
cur.next = ListNode(nums[i])
cur = cur.next
obj.removeNthFromEnd(head, n)
except EOFError:
break
160. 相交链表
思路1:用哈希表很简单 的一个题
思路2:双指针之快慢指针:每个指针分别从不同链表出发,走完当前链表就换另一个链表开头继续走,如果有交点,两者步数刚好一致走到交点;否则刚好都走到第二个链表的末尾
python
'''
160. 相交链表
题目描述:给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
题眼:链表相交
思路1:用哈希表很简单的一个题
思路2:双指针:每个指针分别从不同链表出发,走完当前链表就换另一个链表开头继续走,如果有交点,两者步数刚好一致走到交点;否则刚好都走到第二个链表的末尾
'''
from typing import Optional
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
# 思路1、用哈希表很简单的一个题
# hashTable = set()
# cur = headA
# while cur != None:
# hashTable.add(cur)
# cur = cur.next
# cur = headB
# while cur != None:
# if cur in hashTable:
# return cur
# cur = cur.next
# return None
# 思路2、根据快慢法则,走的快的一定会追上走得慢的
# 在这道题里,有的链表短,他走完了就去走另一条链表,我们可以理解为走的快的指针。
# 那么,只要其中一个链表走完了,就去走另一条链表的路。如果有交点,他们最终一定会在同一个位置相遇
curA, curB = headA, headB
while curA != curB:
if curA != None:
curA = curA.next
else:
curA = headB # 如果headA走完了,那么就切换到headB走
if curB != None:
curB = curB.next
else:
curB = headA # 同理,headB走完了就切换到headA
return curA
142. 环形链表 II
思路1、用哈希表很简单
思路2、双指针之快慢指针:第一步检查是否有环,第二步定位环的入口节点
python
'''
142. 环形链表 II
题目描述:给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
题眼:环形链表+入环的第一个节点
思路1、用哈希表很简单
思路2、第一步检查是否有环,第二步定位环的入口节点
1、快慢指针检查环是否存在,快指针一次走2步,慢指针一次走1步,相对慢指针而言,快指针在一次一步的靠近慢指针,相遇代表存在环
2、入口涉及到数学推导获得结论,有点难
'''
# define singly-linked list
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
# 情况1、链表为空
if head == None:
return None
# 思路1、用哈希表很简单
# hashTable = set()
# cur = head
# while cur != None:
# if cur not in hashTable:
# hashTable.add(cur)
# else: # 出现重复节点说明有环;且第一次出现的节点为起始节点
# return cur
# cur = cur.next
# return None
# 思路2、
slow, fast = head, head
while fast != None and fast.next != None: # 这个判断条件容易写成fast.next.next!=None,这是错的,这里没有虚拟头节点了
fast = fast.next.next
slow = slow.next
if fast == slow: # 快慢指针相遇,说明有环
start = head # 寻找环的开头,根据结论:x=z
while start != slow:
start = start.next
slow = slow.next
return start # 返回环的入口
return None
if __name__ == "__main__":
while True:
try:
in_line = input().strip().split(']')
nums = [int(n) for n in in_line[0].split('[')[1].split(',')]
pos = int(in_line[1].split('=')[1].strip())
# 创建头节点
head = ListNode(nums[0], None)
cur = head
if pos == 0:
ring = head
for i in range(1, len(nums)):
cur.next = ListNode(nums[i], None) # 添加第i个节点
if pos == i:
ring = cur.next
cur = cur.next
if pos != -1:
cur.next = ring
# 输出原始链表
cur = head
n = 0
while cur != None:
print(cur.val, end=' ')
cur = cur.next
n += 1
if n > 20:
break
print()
# 调用函数
obj = Solution()
result = obj.detectCycle(head)
if result != None:
print(result.val)
else:
print(result)
except EOFError:
break