Leetcode题目:python 实现链表题目合集(边做边更新)

1. 题目:实现两个数相加

思路及注意点(新手走的弯路):
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))

2. 题目:删除链表的倒数第N个结点

思路:
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. 题目:两两交换链表中的节点

思路(具体见下图):
1.头节点会变化,所以要增加虚拟头节点p
2.两两交换,1,2,3,4的顺序不能发生变化,先执行1和3 会产生环,陷入死循环。

# 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. 题目:合并两个有序链表

思路:
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 个一组翻转链表

思想:
遍历链表,计数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. 题目:旋转链表

思路:
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. 题目:删除且保留其中一个重复元素

思路:
遍历结点,遇到相同的跳过一个结点(删除)

    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. 题目:删除且不保留其中一个重复元素

思路:
双指针:快慢指针,用快指针找到相同的或者不同的值,并用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. 题目:分割链表

思路:
感觉题目的例子不是很好,最后的这组数据,很能反应题目中的要点:小于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. 题目:二叉树展开为链表

思路:
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. 题目:有序链表转换二叉搜索树

# 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

```python class Solution: def connect(self, root: 'Node') -> 'Node': if not root: return root q=[root] while q: # 遍历当前队列的长度 size = len(q) for i in range(size): # 从队首取出元素 t = q.pop(0) # 判断每层连接的条件 if i

14. 题目:环形链表

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

思路:

  • 存在环路,开始找环入口
    设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. 题目:重排链表

思路:
时间超时,暴力解决,快慢指针,来回挪动指针,自己想出来的,说明链表学会啦,加油。
在中点处分割链表,后面的那个反转,再拼接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 缓存

思路: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) 时间内完成。

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. 题目:对链表进行插入排序

思路:

  1. 判断给定的链表是否为空,若为空,则不需要进行排序,直接返回。
  2. 引入哑节点是便于在 head 节点之前插入节点。
  3. 维护 lastSorted 为链表的已排序部分的最后一个节点,初始时 lastSorted = head。
  4. 维护 curr 为待插入的元素,初始时 curr = head.next。
  5. 比较 lastSorted 和 curr 的节点值。
    若 lastSorted.val <= curr.val,说明 curr 应该位于 lastSorted 之后,将 lastSorted 后移一位,curr 变成新的 lastSorted。
    否则,从链表的头节点开始往后遍历链表中的节点,寻找插入 curr 的位置。令 prev 为插入 curr 的位置的前一个节点,如下图所示。
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. 题目:链表排序(归并)

执行的顺序是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. 题目:奇偶链表

思路:
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. 题目:设计推特

>思路: >1.Twitter >
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)

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值