给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
示例 2:
输入:l1 = [0], l2 = [0]
输出:[0]
示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]
提示:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
dummy = ListNode() # 创建一个虚拟节点
prev = dummy # 创建一个prev指针,初始时指向dummy
# 初始化carry和两个链表对应节点相加的值【carry代表前一个位置的进位数值】
carry, value = 0, 0
# 下面的while循环中之所以有carry,是为了处理两个链表最后节点相加出现进位的情况【当两个节点都走完而且最后的运算并没有进位时,就不会进入这个循环】
while carry or l1 or l2:
# 让value先等于carry既有利于下面两个if语句中两个对应节点值相加,也是为了要处理两个链表最后节点相加出现进位的情况
value = carry
# 只要其中一个链表没走完,就需要计算value的值【如果其中一个链表走完,那么下面的计算就是加总carry和其中一个节点的值,如果两个链表都没走完,那么下面的计算就是carry+对应的两个节点的值】
if l1 is not None:
value = value + l1.val # 计算当前位的值【carry + l1.val + l2.val】
l1 = l1.next # 将l1的指针位置移动到下一个节点,用于下一个循环
if l2 is not None:
value = value + l2.val # 计算当前位的值【carry + l1.val + l2.val】
l2 = l2.next # 将l2的指针位置移动到下一个节点,用于下一个循环
# 如果value值大于十,则进位,需要特殊处理【如果value小于十,下面这行的操作对于carry和value的值都没有影响】
carry, value = divmod(value, 10) # carry代表进位数值,value代表留在当前位的数值
# 尾部插入
prev.next = ListNode(value) #利用value的值创建一个当前节点,并让prev.next指向它
prev = prev.next #移动prev指针到当前节点
#最后只要返回dummy的下一个节点就是我们想要的答案。
return dummy.next
给定两个用链表表示的整数,每个节点包含一个数位。
这些数位是反向存放的,也就是个位排在链表首部。
编写函数对这两个整数求和,并用链表形式返回结果。
示例:
输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即617 + 295
输出:2 -> 1 -> 9,即912
进阶:思考一下,假设这些数位是正向存放的,又该如何解决呢?
示例:
输入:(6 -> 1 -> 7) + (2 -> 9 -> 5),即617 + 295
输出:9 -> 1 -> 2,即912
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
carry, value = 0, 0
dummy = ListNode()
prev = dummy
while l1 or l2 or carry:
value = carry
if l1:
value += l1.val
l1 = l1.next
if l2:
value += l2.val
l2 = l2.next
carry, value = divmod(value, 10)
prev.next = ListNode(value)
prev = prev.next
return dummy.next
给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
示例:
输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 8 -> 0 -> 7
进阶:如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
stack1 = []
stack2 = []
dummy = ListNode()
# 将链表中各个节点的值压入栈中
def push_stack(p, stack):
while p:
stack.append(p.val)
p = p.next
push_stack(l1, stack1)
push_stack(l2, stack2)
# 初始化carry和两个链表对应节点相加的值【carry代表前一个位置的进位数值】
carry, value = 0, 0
# 下面的while循环中之所以有carry,是为了处理两个链表最后节点相加出现进位的情况【当两个节点都走完而且最后的运算并没有进位时,就不会进入这个循环】
while stack1 or stack2 or carry:
# 让value先等于carry既有利于下面两个if语句中两个对应节点值相加,也是为了要处理两个链表最后节点相加出现进位的情况
value = carry
# 只要其中一个栈还有数据,就需要计算value的值【如果其中一个栈已经为空,那么下面的计算就是加总carry和另一个栈的值,如果两个栈都有值,那么下面的计算就是carry+对应的两个栈弹出的值】
if stack1:
value = value + stack1.pop()
if stack2:
value = value + stack2.pop()
# 如果value值大于十,则进位,需要特殊处理【如果value小于十,下面这行的操作对于carry和value的值都没有影响】
carry, value = divmod(value, 10)
# 头部插入
currNode = ListNode(value)
currNode.next = dummy.next
dummy.next = currNode
return dummy.next
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 1:
输入:head = [1,3,2]
输出:[2,3,1]
限制:0 <= 链表长度 <= 10000
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reversePrint(self, head: ListNode) -> List[int]:
stack = []
while head:
stack.append(head.val)
head = head.next
result = []
while stack:
result.append(stack.pop())
return result
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
输入:head = [1,2]
输出:[2,1]
示例 3:
输入:head = []
输出:[]
提示:
进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
dummy = ListNode()
prev = dummy
stack = []
while head:
stack.append(head.val)
head = head.next
while stack:
currNode = ListNode(stack.pop())
prev.next = currNode
prev = prev.next
return dummy.next
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
dummy = ListNode()
curr = head
while curr:
nextNode = curr.next
curr.next = dummy.next
dummy.next = curr
curr = nextNode
return dummy.next
链表具有天然的递归性,一个链表例如 1->2->3->4->5->NULL ,可以看成头节点(节点值为 1 的节点)后面挂接一个更短的链表(缺少节点值为 1 的节点,以节点值为 2 的节点为头节点) 1->更短的链表,依次类推。如下如示:
这样的话,就可以先翻转头节点后面挂接的链表,然后把翻转后的链表的后面再挂接头节点,这样就实现了链表的翻转,如下图示:
先翻转以节点值为 2 的节点作为头节点的链表
将原头节点(节点值为 1 的节点)挂接在翻转之后的链表的后面
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
'''
1. 递归上来就先写终止条件:如果head为空或者head.next为空,返回head
2. 新头结点newHead指向尾结点,此处进入递归,递归一直到遍历到尾结点时才会返回
3. 每一层递归,该层递归中的head会让下一个节点指向自己,head.next.next = head;然后head自己指向空。以此达到反转的目的。
4. 返回新链表的头结点newHead
'''
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
def reverse(nodeA: ListNode)->ListNode:
# 递归结束条件:nodeA 为最后一个节点
if nodeA.next is None:
return nodeA
print("递归进入-->, nodeA.val = ", nodeA.val)
dummy = ListNode()
dummy.next = reverse(nodeA.next) # 递归结束时,nodeA.next 为最后一个节点,nodeA为倒数第2个节点
print("\n递归退出-->, dummy.next = ", dummy.next)
nodeB = nodeA.next
print("nodeA = ", nodeA)
print("nodeB = ", nodeB)
# 颠倒前后2节点连接的方向
nodeA.next = None # 将原来的前节点连接到后节点的链接切断
nodeB.next = nodeA # 将原来的后节点连接到前节点
print("前后节点交换:")
print("nodeB = ", nodeB)
print("nodeA = ", nodeA)
print("dummy.next = ", dummy.next)
print()
return dummy.next
if head is None:
return None
return reverse(head)
输入:
[1,2,3,4,5]
输出结果:
递归进入-->, nodeA.val = 1
递归进入-->, nodeA.val = 2
递归进入-->, nodeA.val = 3
递归进入-->, nodeA.val = 4
递归退出-->, dummy.next = ListNode{val: 5, next: None}
nodeA = ListNode{val: 4, next: ListNode{val: 5, next: None}}
nodeB = ListNode{val: 5, next: None}
前后节点交换:
nodeB = ListNode{val: 5, next: ListNode{val: 4, next: None}}
nodeA = ListNode{val: 4, next: None}
dummy.next = ListNode{val: 5, next: ListNode{val: 4, next: None}}
递归退出-->, dummy.next = ListNode{val: 5, next: ListNode{val: 4, next: None}}
nodeA = ListNode{val: 3, next: ListNode{val: 4, next: None}}
nodeB = ListNode{val: 4, next: None}
前后节点交换:
nodeB = ListNode{val: 4, next: ListNode{val: 3, next: None}}
nodeA = ListNode{val: 3, next: None}
dummy.next = ListNode{val: 5, next: ListNode{val: 4, next: ListNode{val: 3, next: None}}}
递归退出-->, dummy.next = ListNode{val: 5, next: ListNode{val: 4, next: ListNode{val: 3, next: None}}}
nodeA = ListNode{val: 2, next: ListNode{val: 3, next: None}}
nodeB = ListNode{val: 3, next: None}
前后节点交换:
nodeB = ListNode{val: 3, next: ListNode{val: 2, next: None}}
nodeA = ListNode{val: 2, next: None}
dummy.next = ListNode{val: 5, next: ListNode{val: 4, next: ListNode{val: 3, next: ListNode{val: 2, next: None}}}}
递归退出-->, dummy.next = ListNode{val: 5, next: ListNode{val: 4, next: ListNode{val: 3, next: ListNode{val: 2, next: None}}}}
nodeA = ListNode{val: 1, next: ListNode{val: 2, next: None}}
nodeB = ListNode{val: 2, next: None}
前后节点交换:
nodeB = ListNode{val: 2, next: ListNode{val: 1, next: None}}
nodeA = ListNode{val: 1, next: None}
dummy.next = ListNode{val: 5, next: ListNode{val: 4, next: ListNode{val: 3, next: ListNode{val: 2, next: ListNode{val: 1, next: None}}}}}
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
限制:0 <= 节点个数 <= 5000
注意:本题与主站 206 题相同:https://leetcode-cn.com/problems/reverse-linked-list/
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
示例 2:
输入:head = [5], left = 1, right = 1
输出:[5]
提示:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
curr = head
index = 1
stack = []
while curr:
if left <= index <= right:
stack.append(curr.val)
index += 1
curr = curr.next
j = 1
curr = head
while curr:
if left <= j <= right:
curr.val = stack.pop()
j += 1
curr = curr.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 reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
dummy = ListNode()
dummy.next = head
lastNode = dummy
curr = head
index = 1
while curr and index < left:
index += 1
curr = curr.next
lastNode = lastNode.next
tail = curr # tail 指向已经翻转的链表的结尾【也是待翻转链表的开头节点】,用它来把已翻转的链表和剩余链表进行拼接。
curr = curr.next # 由于充当tail的节点不需要翻转,所以将curr的游标向后移动一位
index += 1
while curr and index <= right:
print("curr.val = ", curr.val)
nxt = curr.next
curr.next = lastNode.next # 将当前节点curr的next指向前段不翻转链表尾节点的next节点
lastNode.next = curr
tail.next = nxt # tail节点标记的是已翻转链表的结尾,一直不用动,用tail.next将已经翻转的部分与剩余部分连接起来
curr = nxt # 将curr的游标移动到nxt
index += 1
return dummy.next
给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
给定链表 1->2->3->4, 重新排列为 1->4->2->3.
示例 2:
给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.
因为链表不支持下标访问,所以我们无法随机访问链表中任意位置的元素。
因此比较容易想到的一个方法是,我们利用线性表存储该链表,然后利用线性表可以下标访问的特点,直接按顺序访问指定元素,重建该链表即可。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reorderList(self, head: ListNode) -> None:
"""
Do not return anything, modify head in-place instead.
"""
nodeList = []
while head:
curr = head
head = head.next
curr.next = None # 将当前节点与后续链表切断
nodeList.append(curr)
print("nodeList = ", nodeList)
dummy = ListNode()
prev = dummy
while nodeList:
if nodeList:
prev.next = nodeList.pop(0)
prev = prev.next
if nodeList:
prev.next = nodeList.pop()
prev = prev.next
head = dummy.next
输入数据:
[1,2,3,4,5]
打印数据:
nodeList =
[
ListNode{val: 1, next: None},
ListNode{val: 2, next: None},
ListNode{val: 3, next: None},
ListNode{val: 4, next: None},
ListNode{val: 5, next: None}
]
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reorderList(self, head: ListNode) -> None:
"""
Do not return anything, modify head in-place instead.
"""
nodeList = []
curr = head
while curr:
nodeList.append(curr)
curr = curr.next
print("nodeList = ", nodeList)
i = 0
j = len(nodeList) - 1
while i < j:
nodeList[i].next = nodeList[j]
i += 1
if i == j:
break
nodeList[j].next = nodeList[i]
j -= 1
nodeList[i].next = None
输入数据:
[1,2,3,4,5]
打印数据:
nodeList =
[
ListNode{val: 1, next: ListNode{val: 2, next: ListNode{val: 3, next: ListNode{val: 4, next: ListNode{val: 5, next: None}}}}},
ListNode{val: 2, next: ListNode{val: 3, next: ListNode{val: 4, next: ListNode{val: 5, next: None}}}},
ListNode{val: 3, next: ListNode{val: 4, next: ListNode{val: 5, next: None}}},
ListNode{val: 4, next: ListNode{val: 5, next: None}},
ListNode{val: 5, next: None}
]
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
示例 3:
输入:head = [1,2,3,4,5], k = 1
输出:[1,2,3,4,5]
示例 4:
输入:head = [1], k = 1
输出:[1]
提示:
进阶:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
dummy = ListNode()
dummy.next = head
prev = dummy
while head:
tail = prev # 初始化尾指针
# 将尾指针移动到该小组的尾部
for i in range(k):
tail = tail.next
if tail is None: # 查看剩余部分长度是否大于等于 k,如果小于k,则不用再进行翻转
return dummy.next
nex = tail.next
# 翻转当前小组的所有节点
head, tail = self.reverse(head, tail)
# 把子链表重新接回原链表
prev.next = head
tail.next = nex
prev = tail
head = tail.next
return dummy.next
# 翻转一个子链表,并且返回新的头与尾
def reverse(self, head, tail):
prev = tail
curr = head
while curr != tail:
nxt = curr.next
curr.next = prev.next
prev.next = curr
curr = nxt
return tail, head
存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。
返回同样按升序排列的结果链表。
输入:head = [1,1,2]
输出:[1,2]
输入:head = [1,1,2,3,3]
输出:[1,2,3]
提示:
由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。
具体地,我们从指针 cur 指向链表的头节点,随后开始对链表进行遍历。如果当前 cur 与 cur.next 对应的元素相同,那么我们就将cur.next 从链表中移除;否则说明链表中已经不存在其它与 cur 对应的元素相同的节点,因此可以将cur 指向 cur.next。
当遍历完整个链表之后,我们返回链表的头节点即可。
细节:当我们遍历到链表的最后一个节点时,cur.next 为空节点,如果不加以判断,访问 cur.next 对应的元素会产生运行错误。因此我们只需要遍历到链表的最后一个节点,而不需要遍历完整个链表。
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
if not head:
return head
cur = head
while cur.next:
if cur.val == cur.next.val:
cur.next = cur.next.next
else:
cur = cur.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 deleteDuplicates(self, head: ListNode) -> ListNode:
dummy = ListNode()
dummy.next = head
prev = dummy
curr = head
while curr:
# 如果有重复节点,则跳过当前的重复节点,使得curr指向当前重复元素的最后一个重复节点位置
while curr.next and curr.val == curr.next.val:
curr = curr.next
# 如果经过上一步的while操作,prev的next不再指向原来指向的curr,则说明curr节点有重复元素,
# 则将prev.next指向最后一个重复节点curr的下一个节点,进入下一个循环
if prev.next != curr:
prev.next = curr
# 如果经过上一步的while操作,prev的next依旧指向原来指向的curr,则说明curr节点没有重复元素,则将prev、curr分别向后移动一位,分析下一个元素
else:
prev = prev.next
curr = curr.next
return dummy.next
存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。
返回同样按升序排列的结果链表。
输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
输入:head = [1,1,1,2,3]
输出:[2,3]
提示:
在这里插入代码片
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def deleteDuplicates(self, head):
dummy = ListNode()
dummy.next = head
prev = dummy
curr = head
while curr:
# 如果有重复节点,则跳过当前的重复节点,使得curr指向当前重复元素的最后一个节点位置
while curr.next and curr.val == curr.next.val:
curr = curr.next
# 如果经过上一步的while操作,prev的next不再指向原来指向的curr,则说明curr节点有重复元素,
# 则将prev.next指向最后一个重复节点curr的下一个节点【相当于跳过了剩余的最后一个重复节点】,curr移动到下一个节点位置
if prev.next != curr:
prev.next = curr.next
curr = curr.next
# 如果经过上一步的while操作,prev的next依旧指向原来指向的curr,则说明curr节点没有重复元素,则将prev、curr分别向后移动一位,分析下一个元素
else:
prev = prev.next
curr = curr.next
return dummy.next
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
提示:
进阶:你能用 O(1)(即,常量)内存解决此问题吗?
最容易想到的方法是遍历所有节点,每次遍历到一个节点时,判断该节点此前是否被访问过。
具体地,我们可以使用哈希表来存储所有已经访问过的节点。每次我们到达一个节点,如果该节点已经存在于哈希表中,则说明该链表是环形链表,否则就将该节点加入哈希表中。重复这一过程,直到我们遍历完整个链表即可。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: ListNode) -> bool:
dict = {}
curr = head
while curr:
if dict.get(curr):
return True
dict[curr] = True
curr = curr.next
return False
class Solution:
def hasCycle(self, head: ListNode) -> bool:
seen = set()
while head:
if head in seen:
return True
seen.add(head)
head = head.next
return False
本方法需要读者对「Floyd 判圈算法」(又称龟兔赛跑算法)有所了解。
假想「乌龟」和「兔子」在链表上移动,「兔子」跑得快,「乌龟」跑得慢。当「乌龟」和「兔子」从链表上的同一个节点开始移动时,如果该链表中没有环,那么「兔子」将一直处于「乌龟」的前方;如果该链表中有环,那么「兔子」会先于「乌龟」进入环,并且一直在环内移动。等到「乌龟」进入环时,由于「兔子」的速度快,它一定会在某个时刻与乌龟相遇,即套了「乌龟」若干圈。
我们可以根据上述思路来解决本题。具体地,我们定义两个指针,一快一满。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head,而快指针在位置 head.next。这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: ListNode) -> bool:
dummy = ListNode()
dummy.next = head
slow = dummy
fast = head
# 如果链表是环形链表,则fast、slow指针肯定会先后进入环,当slow与fast相遇时,while结束
while slow != fast:
if fast is None or fast.next is None: # 如果该链表不是环形链表,则fast肯定会先到达尾节点,while结束
return False
slow = slow.next
fast = fast.next.next
return True
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: ListNode) -> bool:
dummy = ListNode()
dummy.next = head
slow = dummy
fast = head
# 如果链表是环形链表,则fast、slow指针肯定会先后进入环,当slow与fast相遇时,while结束
while True:
if fast is None or fast.next is None: # 如果该链表不是环形链表,则fast肯定会先到达尾节点,while结束
return False
slow = slow.next
fast = fast.next.next
if slow == fast: # slow、fast第一次相遇
break
return True
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
提示:
进阶:你是否可以使用 O(1) 空间解决此题?
最容易想到的方法是遍历所有节点,每次遍历到一个节点时,判断该节点此前是否被访问过。
具体地,我们可以使用哈希表来存储所有已经访问过的节点。每次我们到达一个节点,如果该节点已经存在于哈希表中,则说明该链表是环形链表,且该节点是环入口节点。
# 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:
dict = {}
curr = head
while curr:
if dict.get(curr):
return curr
dict[curr] = True
curr = curr.next
return
Python 不支持 do〜while 语法、可以使用 while(无限循环)和 break 组合起来实现 do ~ while 语法
# 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:
fast = head
slow = head
while True:
if fast is None or fast.next is None:
return
slow = slow.next
fast = fast.next.next
if slow == fast: # slow、fast第一次相遇
break
fast = head
while True:
if slow == fast: # slow、fast第二次相遇
break
fast = fast.next
slow = slow.next
return 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:
fast = head
slow = head
while True:
if fast is None or fast.next is None:
return
slow = slow.next
fast = fast.next.next
if slow == fast: # slow、fast第一次相遇
break
fast = head
# slow、fast第二次相遇
while fast != slow:
fast = fast.next
slow = slow.next
return fast
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。
例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
stack = []
curr = head
while curr:
stack.append(curr)
curr = curr.next
for i in range(1, len(stack) + 1):
node = stack.pop()
if i == k:
return node
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
# 快慢指针
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
slow = head
fast = head
# 先让快指针前进k个节点
for _ in range(k):
fast = fast.next
# 当快指针到达链表尾节点时结束,此时慢指针就是倒数第k个节点
while fast:
fast = fast.next
slow = slow.next
return slow
若链表中的某个节点,既不是链表头节点,也不是链表尾节点,则称其为该链表的「中间节点」。
假定已知链表的某一个中间节点,请实现一种算法,将该节点从链表中删除。
例如,传入节点 c(位于单向链表 a->b->c->d->e->f 中),将其删除后,剩余链表为 a->b->d->e->f
示例:
输入:节点 5 (位于单向链表 4->5->1->9 中)
输出:不返回任何数据,从链表中删除传入的节点 5,使链表变为 4->1->9
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteNode(self, node):
"""
:type node: ListNode
:rtype: void Do not return anything, modify node in-place instead.
"""
node.val = node.next.val
node.next = node.next.next
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
提示:
# 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: ListNode, n: int) -> ListNode:
dummy = ListNode()
dummy.next = head
prev = dummy
slow = head
fast = head
for _ in range(n):
fast = fast.next
# 将fast指针移动到链表尾节点,此时slow位于倒数第n个节点,prev位于倒数第n个节点的前一个节点
while fast:
fast = fast.next
slow = slow.next
prev = prev.next
prev.next = slow.next
return dummy.next
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
提示:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
dummy = ListNode() # 哑结点
prev = dummy # 移动指针
curr01 = l1 # 移动指针
curr02 = l2 # 移动指针
while curr01 and curr02:
if curr01.val < curr02.val:
prev.next = curr01
prev = prev.next
curr01 = curr01.next
else:
prev.next = curr02
prev = prev.next
curr02 = curr02.next
while curr01:
prev.next = curr01
prev = prev.next
curr01 = curr01.next
while curr02:
prev.next = curr02
prev = prev.next
curr02 = curr02.next
return dummy.next
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2:
输入:lists = []
输出:[]
示例 3:
输入:lists = [[]]
输出:[]
提示:
维护一个小顶堆,首先将所有链表的第一个节点加入小顶堆,每次从小顶堆中弹出一个节点node加入到最终结果的链表里,并将node的next加入到小顶堆中。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
import heapq
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
if not lists:
return None
dummy = ListNode()
heap = []
# 将lists中的所有链表的第一个元素加入最小堆heap
for idx in range(len(lists)):
if lists[idx]:
heapq.heappush(heap, (lists[idx].val, idx))
lists[idx] = lists[idx].next
print("heap = ", heap)
cur = dummy
# 最小堆heap中始终维持len(lists)个元素【比如lists中有3个链表,则heap中始终维持3个元素】
while heap:
# 创建一个节点
val, idx = heapq.heappop(heap)
cur.next = ListNode(val)
# 将指针移动到下一个位置【用于添加下一个节点】
cur = cur.next
# 如果第idx个链表还有元素没加入最小堆heap,则加入新元素,维持最小堆始终有len(lists)个元素
if lists[idx]:
heapq.heappush(heap, (lists[idx].val, idx))
lists[idx] = lists[idx].next
return dummy.next
每次将链表列表分为两个部分,并分别合并两部分的链表。这样,具体合并时,每次自底向上地只合并两个链表。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
if not lists:
return None
return self.merge(lists, 0, len(lists)-1)
def merge(self, lists, left, right):
if left == right:
return lists[left]
mid = (left + right) // 2
temp1 = self.merge(lists, left, mid)
temp2 = self.merge(lists, mid+1, right)
return self.merge_two(temp1, temp2)
def merge_two(self, temp1, temp2):
if not temp1 and not temp2:
return None
if not temp1 or not temp2:
return temp1 if temp1 else temp2
if temp1.val < temp2.val:
temp1.next = self.merge_two(temp1.next, temp2)
return temp1
else:
temp2.next = self.merge_two(temp1, temp2.next)
return temp2
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?
有两种常用的列表实现,分别为数组列表和链表。如果我们想在列表中存储值,它们是如何实现的呢?
确定数组列表是否回文很简单,我们可以使用双指针法来比较两端的元素,并向中间移动。一个指针从起点向中间移动,另一个指针从终点向中间移动。这需要 O(n) 的时间,因为访问每个元素的时间是 O(1),而有 n 个元素要访问。
然而同样的方法在链表上操作并不简单,因为不论是正向访问还是反向访问都不是 O(1)。而将链表的值复制到数组列表中是 O(n),因此最简单的方法就是将链表的值复制到数组列表中,再使用双指针法判断。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
vals = []
curr = head
# 将链表中的数值复制到数组中
while curr:
vals.append(curr.val)
curr = curr.next
# 判断数组是否是回文
return vals == vals[::-1]
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
示例 1:
输入:head = [4,2,1,3]
输出:[1,2,3,4]
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]
示例 3:
输入:head = []
输出:[]
提示:
进阶:你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
方法一:最小堆
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def sortList(self, head: ListNode) -> ListNode:
heap = []
curr = head
while curr:
heapq.heappush(heap, curr.val)
curr = curr.next
dummy = ListNode()
prev = dummy
while heap:
node = ListNode(heapq.heappop(heap))
prev.next = node
prev = prev.next
return dummy.next
方法二:归并排序
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
# 归并排序
def sortList(self, head: ListNode) -> ListNode:
if not head or not head.next: return head
left_end = self.find_mid(head)
mid = left_end.next
left_end.next = None
left, right = self.sortList(head), self.sortList(mid)
return self.merged(left, right)
# 快慢指针查找链表中点
def find_mid(self, head):
if head is None or head.next is None: return head
slow,fast = head, head.next
while fast is not None and fast.next is not None:
slow=slow.next
fast=fast.next.next
return slow
# 合并有序链表
def merged(self, left, right):
res = ListNode()
h = res
while left and right:
if left.val < right.val:
h.next, left = left, left.next
else:
h.next, right = right, right.next
h = h.next
h.next = left if left else right
return res.next
编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表:
在节点 c1 开始相交。
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。
注意:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
currA = headA
currB = headB
while currA != currB:
if currA is not None:
currA = currA.next
else:
currA = headB
if currB is not None:
currB = currB.next
else:
currB = headA
return currA
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
输入:head = [0,1,2], k = 4
输出:[2,0,1]
提示:
插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。
插入排序算法:
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
if not head:
return head
dummyHead = ListNode(0)
dummyHead.next = head
lastSorted = head
curr = lastSorted.next
while curr:
# 如果已排序的链表部分的尾结点的值小于当前节点的值,则直接将尾结点指针向后移动一位
if lastSorted.val <= curr.val:
lastSorted = lastSorted.next
# 如果链表的已排序部分的尾结点的值大于当前节点的值,则需要将当前节点插入到已排序部分中的合适位置,插入完毕后,尾结点指针也向后移动了一位
if lastSorted.val > curr.val:
prev = dummyHead
while prev.next.val <= curr.val: # 查询插入点
prev = prev.next
lastSorted.next = curr.next # 将已排序的链表部分的尾结点链接到“待处理节点”curr的下一个节点
curr.next = prev.next # 将“待处理节点”curr的下一个节点为插入点的下一个节点
prev.next = curr # 插入点的前一个节点连接到“待处理节点”curr
curr = lastSorted.next
return dummyHead.next