https://leetcode-cn.com/tag/linked-list/problemset/
文章目录
21. 合并两个有序链表(简单)
https://leetcode-cn.com/problems/merge-two-sorted-lists/
「虚拟头结点」
# 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:
if l1 == None and l2 == None:
return None
pre = ListNode()
pre.next = l1 if l1 != None else l2
root = pre
while l1 and l2:
if l1.val <= l2.val:
pre.next = l1
pre = l1
l1 = l1.next
else:
pre.next = l2
pre = l2
l2 = l2.next
if l1:
pre.next = l1
if l2:
pre.next = l2
return root.next
23. 合并K个升序链表(困难)
https://leetcode-cn.com/problems/merge-k-sorted-lists/
# 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:
dummy = ListNode()
p = ListNode()
dummy.next = p
#当lists里面全部为空节点时,循环结束
while any(lists):
#寻找此时头节点最小的链表
minValue = float(inf)
for i in range(len(lists)):
if lists[i] == None:
pass
else:
if lists[i].val < minValue:
minValue = lists[i].val
minIndex = i
#此时头节点最小的链表为lists[minIndex]
p.next = lists[minIndex]
p = lists[minIndex]
lists[minIndex] = lists[minIndex].next
return dummy.next.next
上面算法的问题在于时间复杂度比较高,因为对于在找头节点最小的链表时,是依次遍历。如果在这儿进行优化的话,可以维护一个优先级队列(更多信息可参见:https://docs.python.org/3/library/queue.html),把链表节点放入一个最小堆,就可以维护topk最小的节点,时间复杂度为o(N*logk) 代码如下:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
from queue import PriorityQueue
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
dummy = ListNode()
p = dummy
priorityQ = PriorityQueue()
#遍历lists
for i in range(len(lists)):
if lists[i]:
priorityQ.put((lists[i].val,i,lists[i]))
"""
#注意优先队列需要进行比较,来判断插入位置)(默认从小到大),因此不能直接传node(不可比较),
也不能传(node.val, node)因为可能存在val相等的两个node,
因此加入一个index i来辅助比较,先比较val,如果相等,再比较i
"""
#对优先队列进行get与put
while not priorityQ.empty(): #注意这儿判断是否为空,用内置empty函数
val,i,node = priorityQ.get()
p.next = node
p = node
if node.next:
priorityQ.put((node.next.val,i,node.next))
return dummy.next
141. 环形链表(简单)
https://leetcode-cn.com/problems/linked-list-cycle/
快慢指针即可
# 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:
if head == None:
return False
slow = head
fast = head.next
while fast and fast != slow:
slow = slow.next
if fast.next:
fast = fast.next.next
else:
fast = None
break
if fast:
return True
else:
return False
142. 环形链表 II(中等)
https://leetcode-cn.com/problems/linked-list-cycle-ii/
这是上面那一道题目的扩展,值得注意的是,这道题得让slow与fast指针同时从head出发,而不能像上一题一样,一个在后一个在前
# 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
#fast与slow相遇,为相遇点
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if fast == slow:
break
if fast == None or fast.next == None:
return None
#slow从head出发,fast从相遇点出大,各自步长都为1,向前走
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
#当再一次相遇时,即为环口
return fast
图示:
值得注意的一点是,不要纠结于距离,而是着眼于走多少步,就可以避免多绕的圈带来的思维上的困惑。
876. 链表的中间结点(简单)
https://leetcode-cn.com/problems/middle-of-the-linked-list/
最基础快慢指针
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def middleNode(self, head: ListNode) -> ListNode:
fast = head
slow = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
return slow
160. 相交链表
https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
最naive的想法是,分别求出两个链表的长度,然后让长的链表多走s步(两者长度的差值),此时两个链表在同一起跑线,如果中间有相同的节点,则相交
# 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:
a = headA
b = headB
#先遍历A的长度
la = 0
while a:
la += 1
a = a.next
#再遍历B的长度
lb = 0
while b:
lb += 1
b = b.next
a = headA
b = headB
if lb > la:
for i in range(lb - la):
b = b.next
else:
for i in range(la - lb):
a = a.next
while a != b:
a = a.next
b = b.next
return a #相交或者走到none都包含在内
上面这种解法等于是两个链表分别遍历了两遍
但是写得有点啰嗦,有没有更加优雅的写法呢?
# 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:
a = headA
b = headB
while a != b:
if a == None:
a = headB
else:
a = a.next
if b == None:
b = headA
else:
b = b.next
return a
上面等于是强行让两个链表长度一样,如果相交,则必然中间会遇到相同的节点,本质上和上面的时间复杂度是一样的
19. 删除链表的倒数第 N 个结点(中等)
https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
# 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:
p1 = head
p2 = ListNode() #注意虚节点的应用
p2.next = head
for i in range(n):
p1 = p1.next
while p1:
p1 = p1.next
p2 = p2.next
if p2.next == head:
return head.next
p2.next = p2.next.next
return head
2. 两数相加(中等)
https://leetcode-cn.com/problems/add-two-numbers/
# 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:
p1 = l1
p2 = l2
p = ListNode()
res = p
div = 0
while p1 or p2: #注意这个or
p1_v = 0 if p1 == None else p1.val
p2_v = 0 if p2 == None else p2.val
div,mod = divmod((p1_v+p2_v+div),10) #divmod同时返回商和余数
q = ListNode(mod)
p.next = q
p = q
p1 = p1.next if p1 != None else None
p2 = p2.next if p2 != None else None
#边界条件要考虑到
if div:
q = ListNode(1)
p.next = q
return res.next
24. 两两交换链表中的节点(中等)
https://leetcode-cn.com/problems/swap-nodes-in-pairs/
迭代法
# 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: ListNode) -> ListNode:
if head == None:
return None
pre = ListNode() #需要一个pre指针保留前面的结果
res = pre
p = head #p,q分别是要交换的节点指针
q = p.next
pre.next = p
while q:
#此时交换p,q
p.next = q.next
q.next = p
pre.next = q
#重新置pre,p,q的值
pre = p
p = p.next
q = p.next if p else None
return res.next
递归法
61. 旋转链表(中等)
https://leetcode-cn.com/problems/rotate-list/
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
if k == 0 or head == None:
return head
#首先计算链表的长度
p = head
n = 0
while p:
n += 1
p = p.next
k = k%n + 1
p1 = head
p2 = head
while k:
p1 = p1.next
k -= 1
while p1:
p1 = p1.next
p2 = p2.next
pre = p2
p = pre.next
pre.next = None
pre = p
if p == None:
return head
while p.next:
p = p.next
p.next = head
return pre
83. 删除排序链表中的重复元素(中等)
https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/
# 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:
if head == None:
return head
pre = head
p = head.next
while p:
if p.val != pre.val:
pre.next = p
pre = p
p = p.next
pre.next = None
return head
82. 删除排序链表中的重复元素 II(中等)
https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii/
# 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:
dumpy = ListNode()
dumpy.next = head
if head == None:
return head
pre = dumpy
slow = head
fast = head.next
while fast:
if fast.val != slow.val:
if slow.next == fast:
pre = slow
else:
pre.next = fast
slow = fast
fast = fast.next
if slow.next:
pre.next = fast
return dumpy.next
206. 反转链表(简单)
https://leetcode-cn.com/problems/reverse-linked-list/
# 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:
if head == None:
return head
slow = head
fast = head.next
slow.next = None
while fast:
p = fast.next
fast.next = slow
slow = fast
fast = p
return slow
203. 移除链表元素(简单)
https://leetcode-cn.com/problems/remove-linked-list-elements/
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeElements(self, head: ListNode, val: int) -> ListNode:
slow = ListNode(-1)
fast = head
pre = slow
pre.next = head
while fast:
if fast.val == val:
slow.next = fast.next
else:
slow = fast
fast = fast.next
return pre.next