1. 题目:实现两个数相加
![](https://i-blog.csdnimg.cn/blog_migrate/78a1fc83e1caa6c4dd6bb36b4b0b0543.png)
思路及注意点(新手走的弯路):
1.一定要明确l1,l2链表是不可以在尾部加新结点的,因为最后一个指针值为None。
2.保存结果的链表必须满足在尾部添加结点,因为进位的时候要加1,利用ListNode()可以完成动态添加结点。
3.遍历2个链表,先走完的默认值为0,当另一个也走完的时候,一定要注意不要再加新结点了,此可结果链表的长度等于l1,l2中最长的那个。
4.遍历完后,判断是否需要进位,进位则加一个新结点,并赋值为1。
下面的程序是调试的所有代码。
"======================================定义单向链表的结点========================================"
class ListNode(object):
def __init__(self):
self.val = None
self.next = None
"====================================定义链表的添加,打印操作====================================="
class ListNode_handle:
def __init__(self):
self.cur_node = None
def add(self, data):
#add len(data) new node pointed to previous node
for i in data:
node = ListNode()
node.val = i
node.next = self.cur_node
self.cur_node = node
return node
def print_ListNode(self, node):
while node:
print ('\nnode: ', node, ' value: ', node.val, ' next: ', node.next)
node = node.next
"====================================题目中的函数:两个链表相加===================================="
def addTwoNumbers(l1, l2):
# 保存结果的链表result
# 链表名也是头指针
result = ListNode()
# 三个链表的移动指针
rcur = result
l1cur = l1
l2cur = l2
# 是否进位的标志
tmp_val = 0
# 两个链表相加,直到走完
while l1cur!=None or l2cur != None:
# a,b默认值是当一个链表走完的时候
a = 0
b = 0
if l1cur !=None:
a = l1cur.val
if l2cur != None:
b = l2cur.val
val = a + b + tmp_val
if val>9:
tmp_val = 1
val = val - 10
else:
tmp_val = 0
# 更新结果链表当前节点的值
rcur.val = val
if l1cur !=None:
l1cur = l1cur.next
if l2cur != None:
l2cur = l2cur.next
# 不满足,说明链表都已经走完,此可不需要在结果链表中加节点。
if l2cur !=None or l1cur!=None:
rcur.next = ListNode()
rcur = rcur.next
# 链表都已经走完,判断是否进位,在后面加一个节点,值为1
if tmp_val == 1:
rcur.next = ListNode()
rcur = rcur.next
rcur.val = 1
return result
if __name__ == "__main__":
aa = ListNode_handle()
bb = ListNode_handle()
l1_lsit = [9,9,9,9]
l2_list = [9,9]
aa.add(l1_lsit)
bb.add(l2_list)
l1= aa.cur_node
l2 = bb.cur_node
aa.print_ListNode(addTwoNumbers(l1,l2))
![](https://i-blog.csdnimg.cn/blog_migrate/47cf96b22611b270dcb71f281581c11a.png)
2. 题目:删除链表的倒数第N个结点
![](https://i-blog.csdnimg.cn/blog_migrate/dbbda118760ddce4176b2a35c3ab797c.png)
思路:
1.若链表为空,或者k<0, 则返回原链表。
2.两个指针p,q,先让指针p往前走n步,这样就保持p,q指针相差n个数。。
3.此时p与q一起各往前一步一步走,当p指向空时,q指向倒数第n个结点。
注意点:判断指针p的状态的临界点
- 当p为None时,t<n,不用删除,返回原链表.
- 当p为None时,t=n, 删除第一个结点。
- 当p.next为空时,删除的时链表的中间结点。
"===============================题目解答=============================="
def removeNthFromEnd(head: ListNode, n: int) -> ListNode:
# 链表为空,或者n<0
if not head or n <=0:
return head
# 2个移动的指针
p = q = head
# p指针先走n步,q指针再走,这样两个始终相差n步
t = 0
while p !=None and t < n:
p = p.next
t = t+1
# 链表的长度小于n,不用删除,返回原链表
if t < n:
return head
# 当n == 链表的长度时,即删除(倒数第n个节点==第1个节点)
if p == None:
return head.next
# p指针走到末尾了,q指针就指向倒数第n个节点
while p.next != None:
p = p.next
q = q.next
tmp = q.next
q.next = tmp.next
return head
if __name__ == "__main__":
# 该题《2个数相加》,此处不再写
aa = ListNode_handle()
l1_lsit = [5,4,3,2,1]
aa.add(l1_lsit)
l1= aa.cur_node
aa.print_ListNode(removeNthFromEnd(l1,2))
3. 题目:两两交换链表中的节点
![](https://i-blog.csdnimg.cn/blog_migrate/3b4c03232c787fe5e4604e58ae14cff4.png)
思路(具体见下图):
1.头节点会变化,所以要增加虚拟头节点p
2.两两交换,1,2,3,4的顺序不能发生变化,先执行1和3 会产生环,陷入死循环。
![](https://i-blog.csdnimg.cn/blog_migrate/91fa4550658d6ed0a785b4acc6fe136b.png)
# 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:
# 虚拟头结点,永远是新链表的头节点,不会变化
dummy = ListNode(-1)
dummy.next = head
p = dummy
# 若p后面有一对节点
while p.next and p.next.next:
# 保存2个结点的指针
first = p.next; second = first.next
# 执行1,2,3操作
p.next = second
first.next = second.next
second.next = first
# 执行后面2个结点
p = first
return dummy.next
4. 题目:合并两个有序链表
![](https://i-blog.csdnimg.cn/blog_migrate/d2256397ff1da881917f172500bc0c73.png)
思路:
1.当一个为空,另一个不为空时,直接返回。
2.如果两个链表不为空,比较2个头结点,不确定谁是最终的头结点,需定义一个新链表保存结果。
3.当移动指针一个为空,另一个不为空时,直接将不为空的指针返回给新链表。
# 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, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
# 保存结果的链表
result = ListNode()
# 移动的指针
cur = result
cur1 = list1
cur2 = list2
# 判断待比较的链表是否一个为空
if cur1 == None:
return list2
if cur2 == None:
return list1
# 若两个链表都不为空
while True:
# 当两个当前节点都不为空,作比较
if cur1.val < cur2.val:
cur.val = cur1.val
cur1 = cur1.next
else:
cur.val = cur2.val
cur2 = cur2.next
# 如果有一个为空,不用创建新结点,直接指向不为空的结点
if cur1 == None:
cur.next = cur2
break
if cur2 == None:
cur.next = cur1
break
# 创建新结点
cur.next = ListNode()
cur = cur.next
return result
5. 题目:合并K个有序链表
两两合并转化为上面的题目
使用遍历,时间会超时。
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
if not lists:
return None
n = len(lists)
res = lists[0]
# 将lists[0]作为最终合并的链表,然后将list[0]和lists[1]合并成lists[0-1]
# 再将lists[0-1]和lists[2]合并,如此反复最终lists[0]就是最终结果
for i in range(1,n):
res = self.mergeTwoLists(res,lists[i])
return res
使用堆排序的思想:分而治之的算法,即先使每个子序列有序,再使子序列段间有序,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
n = len(lists)
if n == 0: return None
if n == 1: return lists[0]
mid = n // 2
return self.mergeTwoLists(self.mergeKLists(lists[:mid]), self.mergeKLists(lists[mid:]))
6. 题目:K 个一组翻转链表
![](https://i-blog.csdnimg.cn/blog_migrate/82d8d653d55415b1ce5c4843c97c4442.png)
思想:
遍历链表,计数k个后,记住K个结点的首尾节点传递给反转函数,返回反转后链表的首尾结点,再拼接到原来的链表.
class Solution:
def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
count = 0
result = ListNode()
result.next = head
# 首尾结点
first = result
second = head
if k == 1:
return result.next
while second !=None and first !=None:
second = second.next
count = count + 1
if count == k:
# k个结点的链表反转
lhead,lend= self.ReverseList(first.next, second)
# 反转后的链表拼接到原链表
first.next = lhead
lend.next = second
# 继续下一组k个结点
first = lend
count = 0
return result.next
# 反转链表
def ReverseList(self, head, end):
if not head or not head.next:
return head
cur = head
last = None
# 当cur为空时,last指向了最后一个节点
while True:
#先用tmp保存cur的下一个节点的信息,
tmp = cur.next
# 改变指向前一个节点
cur.next = last
#让last,cur依次向后移动一个节点,继续下一次的指针反转
last = cur
cur = tmp
if cur == end:
break
return last, head
7. 题目:旋转链表
![](https://i-blog.csdnimg.cn/blog_migrate/a9f6aff1d9bc0161eec1dffdcf15f647.png)
思路:
1.首先获取链表的长度,判断旋转次数k和长度的关系,得到实际旋转次数 k = m o d ( k , l e n g t h ) k = mod(k,length) k=mod(k,length)。
2.对链表执行k次往右移动一次的操作。
class Solution:
def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
if head == None or head.next ==None:
return head
length = self.get_length(head)
# 移动length次,等于不变
k = k % length
for i in range(k):
head = self.rotate1Right(head)
return head
# 链表往右移动一次
def rotate1Right(self, head):
result = ListNode()
cur = result
# 循环到倒数第二个结点
while head.next != None:
cur.next = ListNode()
cur = cur.next
cur.val = head.val
head = head.next
# 最后一个节点的值赋给头结点
result.val = head.val
return result
# 获取链表的长度
def get_length(self,head):
count = 0
while head != None:
count = count+1
head =head.next
return count
8. 题目:删除且保留其中一个重复元素
![](https://i-blog.csdnimg.cn/blog_migrate/e7de25d1f44c1f41921c6270d74d19b3.png)
思路:
遍历结点,遇到相同的跳过一个结点(删除)
def deleteDuplicates(self, head):
if head == None:
return head
cur = head
while cur.next != None:
first = cur.val
ccur = cur.next
second = ccur.val
# 删除重复结点,保留其中一个
if second == first:
ccur = ccur.next
cur.next = ccur
else:
cur = ccur
return head
9. 题目:删除且不保留其中一个重复元素
![](https://i-blog.csdnimg.cn/blog_migrate/74c8122ddbea302a70ebffeccbcdbe10.png)
思路:
双指针:快慢指针,用快指针找到相同的或者不同的值,并用flag标记,找到不同的值后用慢指针指向。
def deleteDuplicates(self, head: ListNode) -> ListNode:
dummy = ListNode(0)
slow = dummy
fast = head
while fast != None:
# 是否快进的标志
flag = False
while fast.next !=None and fast.val == fast.next.val:
flag = True
fast = fast.next
if not flag:
slow.next = fast
slow = slow.next
fast = fast.next
slow.next = None
return dummy.next
10. 题目:分割链表
![](https://i-blog.csdnimg.cn/blog_migrate/023698d5c929e38a87c3ef7f20333cd8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ca52b4d0c912ab9605108aa4f62cc03a.png)
思路:
感觉题目的例子不是很好,最后的这组数据,很能反应题目中的要点:小于x的放到 ≥ \geq ≥x的前面。
遍历链表的值,小于x的放一个链表, ≥ \geq ≥x的放另一个链表,再拼接2个链表。
class Solution:
def partition(self, head: ListNode, x: int) -> ListNode:
p=less=ListNode(0)
q=more=ListNode(0)
while head:
if head.val<x:
less.next=head
less=less.next
else:
more.next=head
more=more.next
head=head.next
# 链表的最后都需要指向None,这个代表链表的结束
more.next=None
less.next=q.next
return p.next
11. 题目:二叉树展开为链表
![](https://i-blog.csdnimg.cn/blog_migrate/3e17de874ac360f2101cefa9958b0802.png)
思路:
1.明确单链表的顺序是先序遍历的结果:根左右,保存到result中。
2.明确最终返回的不是链表,而是二叉树,只是树的左节点为空。
3.遍历result中的树节点,依次的左子树为空,右子树为下一个结点。
4.此时的root就是最终的二叉树。
class Node:
def __init__(self,value=None,left=None, right=None):
self.value = value
self.left = left
self.right = right
class Solution:
def flatten(self, root: TreeNode) -> None:
result = []
def preTraverse(root,result):
# 获取前序遍历的结果
if root == None:
return result
"一定要是树节点,而不是树结点的值"
result.append(root)
preTraverse(root.left,result)
preTraverse(root.right,result)
preTraverse(root,result)
"遍历result中的树节点,依次的左子树为空,右子树为下一个结点"
for i in range(1, len(result)):
prev, curr = result[i - 1], result[i]
prev.left = None
prev.right = curr
# 测试:
if __name__ == '__main__':
root = Node()
root = Node('1', Node('2', Node('3'), Node('4')), Node('5',right = Node('6')))
flatten(root)
12. 题目:有序链表转换二叉搜索树
![](https://i-blog.csdnimg.cn/blog_migrate/ddc656638f4688a53f167f4dfe13a8ab.png)
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def sortedListToBST(self, head: Optional[ListNode]) -> Optional[TreeNode]:
def getMedian(left: ListNode, right: ListNode) -> ListNode:
fast = slow = left
while fast != right and fast.next != right:
fast = fast.next.next
slow = slow.next
return slow
def buildTree(left: ListNode, right: ListNode) -> TreeNode:
if left == right:
return None
mid = getMedian(left, right)
root = TreeNode(mid.val)
root.left = buildTree(left, mid)
root.right = buildTree(mid.next, right)
return root
return buildTree(head, None)
13. 题目:填充每个节点的下一个右侧节点指针 II
![](https://i-blog.csdnimg.cn/blog_migrate/1ec536d8a846a1cb26209a443f7bb3fc.png)
14. 题目:环形链表
![](https://i-blog.csdnimg.cn/blog_migrate/bb96d93828ca6df197fc4cf699f86469.png)
class Solution:
def hasCycle(self, head: ListNode) -> bool:
# 思路1:遍历+列表,太简单,放弃
# 思路2:快慢指针,有环:慢和快指针相遇,无环:快指向尾结点
if not head or not head.next:
return False
slow = head
fast = head.next
while slow != fast:
# fast指向尾结点
if not fast or not fast.next:
return False
# slow走一步,fast走2步,比slowkuai,才能在环中赶上slow,否则fast走一步,它俩始终一前一后
slow = slow.next
fast = fast.next.next
return True
15. 题目:环形链表 II
![](https://i-blog.csdnimg.cn/blog_migrate/a9696c4fee56c63558f02a3c360e46b6.png)
思路:
- 存在环路,开始找环入口
设head到入口长度为x,入口到相遇点长度为y,相遇点到入口距离为z
y+z为环长度。有fast走两步slow走一步可知,相当于进环之后fast在一步一步追slow对吧,直到二者相遇,且这段距离绝对不大于y+z。但slow进环之前fast转了多少圈并不知道。设为n
x+y为总循环次数,(x+n(y+z))/2也为总循环次数
关键点在这里,x = (n-1)(y+z) + z,相遇点到入口距离为z,x为最终解的位置。- 现在是z的位置已经找到了但是n不知道。左边从head.next等价于x-1,右边z.next等价于(n-1)(y+z) + z-1,直到x次他们就会相遇,每次走一步,走了x步时,等式两边 0 = 0
意思就是说在相遇点圈里走z步就会到达入口,走n圈加z步仍然是入口。从head走x步也能到入口,此时二者相遇
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
# 思路:快慢指针,有环:慢和快指针相遇,无环:快指向尾结点
fast = head
slow = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if slow == fast:
index1 = head
index2 = fast
while index1 != index2:
index1 = index1.next
index2 = index2.next
return index2
return None
16. 题目:重排链表
![](https://i-blog.csdnimg.cn/blog_migrate/08cc663e854835129302e8ac3db523b9.png)
思路:
时间超时,暴力解决,快慢指针,来回挪动指针,自己想出来的,说明链表学会啦,加油。
在中点处分割链表,后面的那个反转,再拼接2个链表
class Solution:
def reorderList(self, head: ListNode) -> None:
if not head.next:
return head
# 找到链表的中间节点
mid = self.middleNode(head)
l1 = head
l2 = mid.next
# l1链表从mid处切断
mid.next = None
# l2链表反转
l2 = self.reverseList(l2)
# 合并2个链表
self.mergeList(l1, l2)
#利用双指针找链表中点,一快一慢
def middleNode(self, head: ListNode) -> ListNode:
slow = fast = head
while fast.next and fast.next.next:
slow = slow.next
fast = fast.next.next
return slow
def reverseList(self, head: ListNode) -> ListNode:
prev = None
curr = head
while curr:
nextTemp = curr.next
curr.next = prev
prev = curr
curr = nextTemp
return prev
def mergeList(self, l1: ListNode, l2: ListNode):
while l1 and l2:
l1_tmp = l1.next
l2_tmp = l2.next
l1.next = l2
l1 = l1_tmp
l2.next = l1
l2 = l2_tmp
"暴力解决,指针来回移动,时间超时"
class Solution:
def reorderList(self, head: ListNode) -> None:
"""
Do not return anything, modify head in-place instead.
"""
fast = head.next
slow = head
if fast == None:
return head
while slow != None:
while fast.next!=None and fast.next.next!=None:
fast = fast.next
end = fast.next
if end == None:
break
fast.next = None
tmp = slow.next
slow.next = end
end.next = tmp
slow = tmp
fast = slow
return head
17. 题目:LRU 缓存
![](https://i-blog.csdnimg.cn/blog_migrate/6c2d0a2d064c324ab438faf3515fe6ed.png)
思路:LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。
双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。
哈希表即为普通的哈希映射(HashMap),通过缓存数据的键映射到其在双向链表中的位置。
这样以来,我们首先使用哈希表进行定位,找出缓存项在双向链表中的位置,随后将其移动到双向链表的头部,即可在 O(1)O(1) 的时间内完成 get 或者 put 操作。具体的方法如下:
- 对于 get 操作,首先判断 key 是否存在:
如果 key 不存在,则返回 -1−1;
如果 key 存在,则 key 对应的节点是最近被使用的节点。通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部,最后返回该节点的值。- 对于 put 操作,首先判断 key 是否存在:
如果 key 不存在,使用 key 和 value 创建一个新的节点,在双向链表的头部添加该节点,并将 key 和该节点添加进哈希表中。然后判断双向链表的节点数是否超出容量,如果超出容量,则删除双向链表的尾部节点,并删除哈希表中对应的项;
如果 key 存在,则与 get 操作类似,先通过哈希表定位,再将对应的节点的值更新为 value,并将该节点移到双向链表的头部。
上述各项操作中,访问哈希表的时间复杂度为 O(1)O(1),在双向链表的头部添加节点、在双向链表的尾部删除节点的复杂度也为 O(1)O(1)。而将一个节点移到双向链表的头部,可以分成「删除该节点」和「在双向链表的头部添加节点」两步操作,都可以在 O(1)O(1) 时间内完成。
![](https://i-blog.csdnimg.cn/blog_migrate/171dc28f70b164bc837e62e1d0d42e1d.png)
class DLinkedNode:
def __init__(self, key=0, value=0):
self.key = key
self.value = value
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.cache = dict()
# 使用伪头部和伪尾部节点
self.head = DLinkedNode()
self.tail = DLinkedNode()
self.head.next = self.tail
self.tail.prev = self.head
self.capacity = capacity
self.size = 0
def get(self, key: int) -> int:
if key not in self.cache:
return -1
# 如果 key 存在,先通过哈希表定位,再移到头部
node = self.cache[key]
self.moveToHead(node)
return node.value
def put(self, key: int, value: int) -> None:
if key not in self.cache:
# 如果 key 不存在,创建一个新的节点
node = DLinkedNode(key, value)
# 添加进哈希表
self.cache[key] = node
# 添加至双向链表的头部
self.addToHead(node)
self.size += 1
if self.size > self.capacity:
# 如果超出容量,删除双向链表的尾部节点
removed = self.removeTail()
# 删除哈希表中对应的项
self.cache.pop(removed.key)
self.size -= 1
else:
# 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
node = self.cache[key]
node.value = value
self.moveToHead(node)
def addToHead(self, node):
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
def removeNode(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def moveToHead(self, node):
self.removeNode(node)
self.addToHead(node)
def removeTail(self):
node = self.tail.prev
self.removeNode(node)
return node
18. 题目:对链表进行插入排序
![](https://i-blog.csdnimg.cn/blog_migrate/49d413a234e57c7ecd7e6e8f6b16c8f8.png)
思路:
- 判断给定的链表是否为空,若为空,则不需要进行排序,直接返回。
- 引入哑节点是便于在 head 节点之前插入节点。
- 维护 lastSorted 为链表的已排序部分的最后一个节点,初始时 lastSorted = head。
- 维护 curr 为待插入的元素,初始时 curr = head.next。
- 比较 lastSorted 和 curr 的节点值。
若 lastSorted.val <= curr.val,说明 curr 应该位于 lastSorted 之后,将 lastSorted 后移一位,curr 变成新的 lastSorted。
否则,从链表的头节点开始往后遍历链表中的节点,寻找插入 curr 的位置。令 prev 为插入 curr 的位置的前一个节点,如下图所示。
![](https://i-blog.csdnimg.cn/blog_migrate/b65f3fd634fcb04086fcfcab5418e347.png)
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
# 若链表为空
if not head:
return head
# 创建哑节点,方便在头结点前插入
dummyHead = ListNode(0)
dummyHead.next = head
# 排好序的最后一个结点
lastSorted = head
# 当前待排序的结点
curr = head.next
while curr:
# 如果待排序的值大于排序后的最后一个,不用插入,直接放在排好序的后面
if lastSorted.val <= curr.val:
lastSorted = lastSorted.next
else:
# 待排序的值与前面所有有序的结点挨个比较
prev = dummyHead
while prev.next.val <= curr.val:
prev = prev.next
lastSorted.next = curr.next
curr.next = prev.next
prev.next = curr
curr = lastSorted.next
return dummyHead.next
19. 题目:链表排序(归并)
![](https://i-blog.csdnimg.cn/blog_migrate/daa9296b32b97d4855bd25323da3f659.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9874a5bde44bc685cc361c63259da711.png)
执行的顺序是1,2,3,11,21,31,…n1截止,n-1中的合并…, 2中的合并,1中合并。
# 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
"1. 找到链表的中点,快慢指针法"
slow, fast = head, head.next
while fast != None and fast.next != None:
fast = fast.next.next
slow = slow.next
"2. mid指向后半段链表,head指向前半段链表"
# 执行 slow.next = None 将链表切断
mid = slow.next
slow.next = None
"3. 递归分割"
left = self.sortList(head)
right = self.sortList(mid)
"4. 递归分完后开始执行下面的合并"
res = ListNode(0)
cur = res
while left !=None and right!=None:
if left.val < right.val:
cur.next = left
left = left.next
else:
cur.next = right
right = right.next
cur = cur.next
if left ==None:
cur.next = right
else:
cur.next = left
return res.next
20. 题目:奇偶链表
![](https://i-blog.csdnimg.cn/blog_migrate/4b406a73910e7938aeadd434c1a682d9.png)
思路:
1.题目要求:必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。
2.O(n) 需要遍历链表中的每个节点,并更新指针。
3.O(1) 不能创建新链表,再原来的链表上维护指针。
4.奇数和偶数指针分割2两个链表,然后在拼接。
class Solution:
def oddEvenList(self, head: ListNode) -> ListNode:
if not head:
return head
# head表示奇数的头节点,evenhead表示偶数的头节点
evenHead = head.next
# odd,even分别表示奇数和偶数链表的移动结点
odd, even = head, evenHead
# 当前奇数的后面是否有一偶一奇,只有偶就退出循环,起码奇数已经剔除完毕
while even and even.next:
odd.next = even.next
odd = odd.next
even.next = odd.next
even = even.next
# 奇数拼接偶数链表
odd.next = evenHead
return head
21. 题目:设计推特
![](https://i-blog.csdnimg.cn/blog_migrate/b43b968c7b228a6df7ae6998ac879f27.png)
class Twitter:
# 定义用户类,因为一个用户有两个基本信息,必须创建一个结构体、类
class User:
def __init__(self):
# 用集合存储关注者
self.followee = set()
# 用列存储推文
self.tweet = list()
def __init__(self):
# 定义当前时间戳
self.time = 0
# 检索最近推文上限
self.recentMax = 10
# 使用字典存储推文的发送时间
self.tweetTime = dict()
# 存储用户信息
self.user = dict()
def postTweet(self, userId: int, tweetId: int) -> None:
"""
根据给定的 tweetId 和 userId 创建一条新推文。简单理解创作者id,文章id
"""
# 如果创作者id不在user变量中,新建一个用户实例
if userId not in self.user:
self.user[userId] = Twitter.User()
# 往user变量中存key=创建者id,value=文章id
self.user[userId].tweet.append(tweetId)
# 记录前前文章发送的时间
self.time += 1
# 记录所有文章的时间
self.tweetTime[tweetId] = self.time
def getNewsFeed(self, userId: int) -> List[int]:
"""
检索当前用户新闻推送中最近 10 条推文的 ID 。
"""
if userId not in self.user:
return list()
# 获取该用户下最新的10篇文章
ans = self.user[userId].tweet[-10:][::-1]
# 遍历该用户的关注
for followeeId in self.user[userId].followee:
# 关注也必须在用户列表中,否则没办法处理,在关注者函数中强制了这一点
if followeeId in self.user:
opt = self.user[followeeId].tweet[-10:][::-1]
i, j, combined = 0, 0, list()
while i < len(ans) and j < len(opt):
# 依次自己写的最近10篇和关注者的最近10篇,合并最新的10篇
if self.tweetTime[ans[i]] > self.tweetTime[opt[j]]:
combined.append(ans[i])
i += 1
else:
combined.append(opt[j])
j += 1
combined.extend(ans[i:])
combined.extend(opt[j:])
# 合并的ans再与下一个关注者发的最近10篇进行比较
ans = combined[:10]
return ans
def follow(self, followerId: int, followeeId: int) -> None:
# followerId开始关注followee
if followerId != followeeId:
# 如果粉丝不在user中,创建一个粉丝User
if followerId not in self.user:
self.user[followerId] = Twitter.User()
self.user[followerId].followee.add(followeeId)
def unfollow(self, followerId: int, followeeId: int) -> None:
# followerId取消关注followee
if followerId != followeeId:
if followerId in self.user:
# 移除指定关注者
self.user[followerId].followee.discard(followeeId)