【题目】
反转链表
链表内指定区间反转
链表中的节点每k个一组翻转
合并两个有序链表
合并K个有序链表
判断链表中是否有环
链表中环的入口结点
链表中倒数最后k个结点
删除链表的倒数第n个节点
两个链表的第一个公共结点
链表相加(二)
单链表的排序
判断一个链表是否为回文结构
链表的奇偶重排
删除有序链表中重复的元素-I
删除有序链表中重复的元素-II
反转链表
class Solution:
def ReverseList(self , head: ListNode) -> ListNode:
# write code here
if not head:
return head
pre = None
while head:
nx = head.next
head.next = pre
pre = head
head = nx
return pre
链表指定区间反转
将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转,要求时间复杂度 O*(n),空间复杂度 O(1)。
思路1:提到反转,想到栈
将m到n位置的结点加入到栈中,对栈内元素不断弹出,实现反转
class Solution:
def reverseBetween(self , head: ListNode, m: int, n: int) -> ListNode:
# write code here
if not head:
return head
if m == n:
return head
dummpy = ListNode(-1)
dummpy.next = head
cur = head
pre = dummpy
for i in range(m-1):
pre = cur
cur = cur.next
s = []
for j in range(n-m):
s.append(cur)
cur = cur.next
pre.next = cur
nx = cur.next
while s:
cur.next = s.pop()
cur = cur.next
cur.next = nx
return dummpy.next
思路2:每次将当前结点放到该区间的最前面实现反转,具体可以参考K个一组翻转,里面有详解
class Solution:
def reverseBetween(self , head: ListNode, m: int, n: int) -> ListNode:
# write code here
if not head:
return head
if m == n:
return head
dummpy = ListNode(-1)
dummpy.next = head
cur = head
pre = dummpy
for i in range(m-1):
pre = cur
cur = cur.next
for j in range(m,n):
nx = cur.next
cur.next = nx.next
nx.next = pre.next
pre.next = nx
return dummpy.next
链表中的节点每k个一组翻转
合并两个有序链表
思路:双指针
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def Merge(self , pHead1: ListNode, pHead2: ListNode) -> ListNode:
p = head = ListNode(0)
while pHead1 and pHead2:
if pHead1.val < pHead2.val:
head.next = pHead1
pHead1 = pHead1.next
head = head.next
else:
head.next = pHead2
pHead2 = pHead2.next
head = head.next
head.next = pHead1 or pHead2
return p.next
合并K个有序链表 【BM5】
合并 k 个升序的链表并将结果作为一个升序的链表返回其头节点。
思路
- 合并两个有序链表,可以采用双指针,分别指向两个有序链表,依次比较,令新链表结点head指向较小的指针,即merge()
- 合并K组,可以考虑使用分治法;
- 分:将K组不断划分,直到子问题只有一组链表;
- 治:合并两个子问题,即合并两个有序链表
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def mergeKLists(self , lists: List[ListNode]) -> ListNode:
# write code here
if len(lists) == 0:
return None
def merge(p,q):
node = ListNode(-1)
head = node
while p and q:
if p.val <= q.val:
head.next = p
p = p.next
else:
head.next = q
q = q.next
head = head.next
head.next = p or q
return node.next
def divide(lists,left,right):
if left > right:
return None
elif left == right:
return lists[left]
else:
mid = (left+right)//2
return merge(divide(lists, left, mid), divide(lists, mid+1, right))
return divide(lists, 0, len(lists)-1)
判断链表中是否有环 【BM6】
- 定义快慢指针,一个指针每次走一步,另一指针每次走两步;如果链表有环,则两个指针终究会在环中相遇
- 使用哈希表存储遍历过的结点,如果当前结点已经存在于哈希表中说明有环
def hasCycle(self , head: ListNode) -> bool:
if not head:
return False
slow,fast = head,head
while slow and fast:
slow = slow.next
if fast.next:
fast = fast.next.next
else:
return False
if slow == fast:
return True
return False
链表中环的入口结点 【BM7】
- 定义快慢指针
- 借助哈希表存储遍历过结点,当前结点若存在于哈希表中,则当前结点就是环的入口结点
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
if not pHead:
return pHead
d = []
while pHead:
if pHead.val not in d:
d.append(pHead.val)
else:
return pHead
pHead = pHead.next
链表中倒数最后k个结点 【BM8】
题解:
使用快慢指针,快指针比慢指针多走K步,当快指针为空时,慢指针即指向倒数第k个结点
特殊情况:1、输入为空;2、k值大于链表长度
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindKthToTail(self , pHead: ListNode, k: int) -> ListNode:
# write code here
if not pHead:
return pHead
p = pHead
n = 1
while p.next:
n += 1
p = p.next
if n < k:
return None
front,behind = pHead,pHead
for i in range(k):
front = front.next
while front:
front = front.next
behind = behind.next
return behind
删除链表倒数第n个结点 【BM9】
- 定义快慢指针,快指针比慢指针多走n步
- 当快指针为空时,说明慢指针即为要删除的结点
class Solution:
def removeNthFromEnd(self , head: ListNode, n: int) -> ListNode:
# write code here
pHead = ListNode(-1)
pHead.next = head
pre,cur = pHead,pHead
for i in range(n):
cur = cur.next
while cur.next:
pre = pre.next
cur = cur.next
pre.next = pre.next.next
return pHead.next
两个链表的第一个公共结点 【BM10】
输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空
- 定义两个指针p1、p2,分别指向两个链表
- 当每个指针走完各自的链表后,互换赛道继续走
- 当p1为空时即走完第一个链表,将p1指向pHead2,走第二个赛道
- 当p2为空时即走完第二个赛道,将p2指向pHead1,走第一个赛道
- 最终p1和p2会在公共节点相遇,如果两个链表没有公共节点,则两个指针都走完两个链表,最终两个指针都为空,返回None
class Solution:
def FindFirstCommonNode(self , pHead1 , pHead2 ):
# write code here
if not pHead1 or not pHead2:
return None
p1,p2 = pHead1,pHead2
while p1 != p2:
if not p1:
p1 = pHead2
else:
p1 = p1.next
if not p2:
p2 = pHead1
else:
p2 = p2.next
return p1
链表相加 【BM11】
题目:假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。给定两个这种链表,请生成代表两个整数相加值的结果链表
- 将两个链表倒序
- 长度长的链表存储相加结果
def f(head):
'''把节点放入列表中,并反转'''
num = []
while head:
num.append(head)
head = head.next
return num[::-1]
class Solution:
def addInList(self , head1 , head2 ):
num1, num2, carry = f(head1), f(head2), 0
if len(num1) < len(num2):
num1, num2 = num2, num1
for i in range(len(num1)):
if i < len(num2) or carry:
summation = num1[i].val + (num2[i].val if i < len(num2) else 0) + carry
carry, num = divmod(summation, 10)
num1[i].val = num
else:
break
if carry:
carryhead = ListNode(carry)
carryhead.next = num1[-1]
return carryhead
return num1[-1]
判断一个链表是否为回文结构
思路:
将链表结点的所有值存储在数组中,在数组中使用双指针判断是否为回文结果。
class Solution:
def isPail(self , head: ListNode) -> bool:
# write code here
res = []
while head:
res.append(head.val)
head = head.next
if not res:
return True
i = 0
j = len(res)-1
while i <= j:
if res[i] != res[j]:
return False
i += 1
j -= 1
return True
链表的奇偶重排 【牛客BM14】
给定一个单链表,请设定一个函数,将链表的奇数位节点和偶数位节点分别放在一起,重排后输出。注意是节点的编号而非节点的数值。
要求:空间复杂度 O(n),时间复杂度 O(n)
题解
- 使用两个链表存储奇偶结点,最后进行连接,时间复杂度和空间复杂度均为O(n)
- 待更新
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def oddEvenList(self , head: ListNode) -> ListNode:
# write code here
if not head:
return head
p_node = ListNode(-1)
q_node = ListNode(-1)
p = head
p_node.next = p
q = head.next
q_node.next = q
while p.next and q.next:
p.next = p.next.next
q.next = q.next.next
p = p.next
q = q.next
p.next = q_node.next
return p_node.next
删除有序链表中重复的元素-I【BM15 】
删除给出链表中的重复元素(链表中元素从小到大有序),使链表中的所有元素都只出现一次
例如:
给出的链表1→1→2,返回1→2.
给出的链表为1→1→2→3→3,返回1→2→3.
题解:
- 定义两个指针p和q,q指向p的下一个结点;
- 如果p和q的值相同,q指针就一直往后走,直到q的值与p不同;令p.next = q,既可实现相同元素的结点只保留一个。
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteDuplicates(self , head: ListNode) -> ListNode:
# write code here
if not head:
return head
p = head
q = p.next
while q:
while q and q.val == p.val:
q = q.next
p.next = q
p = p.next
if q:
q = q.next
return head
删除有序链表中重复的元素-II 【BM16】
给出一个升序排序的链表,删除链表中的所有重复出现的元素,只保留原链表中只出现一次的元素。
例如:
给出的链表为1→2→3→3→4→4→5, 返回1→2→5.
给出的链表为1→1→1→2→3, 返回2→3.
要求:空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)
进阶:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
题解:
和BM15不同的是,该题需要将所有重复的元素都删除,因此可以再加一个指针pre,关系是pre->p->q。
- 定义一个哨兵结点node,令node.next = head。
- 比较p和q结点的值是否相同,如果相同,则q指针后移,直到q的值与p不同。
- 令pre.next = q,p = pre.next q = p.next
注意:当找到和p不同值的指针q后,15是直接令p指向q,保留一个重复的元素;而该题需要全部删掉,所以是;令pre指向q。因此需要定义一个哨兵结点,当链表最开始就全部是重复结点时,头结点也会被删掉。
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteDuplicates(self , head: ListNode) -> ListNode:
# write code here
if not head:
return head
node = ListNode(-1)
node.next = head
pre = node
p = head
q = p.next
while q:
if p.val == q.val:
while q and p.val == q.val:
q = q.next
pre.next = q
p = q
if p:
q = p.next
else:
pre = pre.next
p = p.next
q = q.next
return node.next