task12,leetcode:146,148,155-python

146.URL缓存机制

设计模式题,较为麻烦,但是理解后会更加深入理解规范的python类是如何实现的。难点可能是在addToHead()函数上,具体的理解根据代码参考以下图解。
在这里插入图片描述

注意,因为在形成②和③的时候要用到旧的node,因此⑤这个链不能在②和③之前断掉,而形成④的时候会使其断掉,因此④要保证在②和③之后,其他顺序随意。

# 定义的一个双向链表类
class DLinkedNode:
    def __init__(self, key=0, value=0):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache:
    """
    越靠近head的结点进入缓存越晚,越靠近tail的进入缓存越早。
    """

    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:
        # dict()中查找元素的时间复杂度为O(1)
        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              # cache当前容量+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              # 前一个结点的next是下一个结点
        node.next.prev = node.prev              # 下一个结点的prev是前一个结点

    def moveToHead(self, node):
        self.removeNode(node)                   # 删除旧的node
        self.addToHead(node)                    # 将新的node添加到头部

    def removeTail(self):
        node = self.tail.prev                   # 定位要删除的node
        self.removeNode(node)                   # 删除node
        return node                             # 返回node以便更新self.cache

148.排序链表

本题最为纳闷的地方其实是:既然都要排序了,为什么不用数组非要用链表呢(人类迷惑行为)。不过既然出了,就给出两种解法:
解法一:自顶向下的归并排序

# 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:
        """
        自顶向下的实现,在递归的时候需要保存各个中间变量,因此空间复杂度为O(logn)
        """
        def find_mid(head, end):
            # 开始的两个if定义递归出口
            if not head:            # 当head为空时返回
                return head
            # 当只有一个元素的时候,返回该元素(end一定为None)
            if head.next == end:    
                head.next = None
                return head
            # 找到归并的mid来将大的链表一分为二
            slow, fast = head, head
            while fast != end:
                slow = slow.next
                fast = fast.next
                if fast != end:
                    fast = fast.next
            mid = slow
            # 合并划分好的两个子链表
            return merge(find_mid(head, mid), find_mid(mid, end))
        
        def merge(head1, head2):
            """该函数用来合并两个链表,因为是递归的,所以head1和head2必定为有序的
            所以该函数可以参考lc.合并两个有序链表 的实现
            """
            dummyhead = temp = ListNode(0)
            while head1 and head2:
                if head1.val < head2.val:
                    temp.next = head1
                    head1 = head1.next
                else:
                    temp.next = head2
                    head2 = head2.next
                temp = temp.next
            
            if head1:
                temp.next = head1
            if head2:
                temp.next = head2
            return dummyhead.next
        return find_mid(head, None)

时间复杂度O(logn),空间复杂度O(logn)
解法二:自底向上的归并排序
思想与自顶向下的排序类似,区别是要多用几个指针来记录归并的过程(解法一中的递归更方便记录这个过程)。

class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        def merge(head1: ListNode, head2: ListNode) -> ListNode:
            # 普通的两有序链表的合并
            dummyHead = ListNode(0)
            temp, temp1, temp2 = dummyHead, head1, head2
            while temp1 and temp2:
                if temp1.val <= temp2.val:
                    temp.next = temp1
                    temp1 = temp1.next
                else:
                    temp.next = temp2
                    temp2 = temp2.next
                temp = temp.next
            if temp1:
                temp.next = temp1
            elif temp2:
                temp.next = temp2
            return dummyHead.next
        
        if not head:
            return head
        
        # 计算链表的总长度
        length = 0
        node = head
        while node:
            length += 1
            node = node.next
        
        dummyHead = ListNode(0, head)
        subLength = 1
        while subLength < length:
            # prev用来连接合并好的两两子链表, curr用来遍历链表
            prev, curr = dummyHead, dummyHead.next

            # 依次合并连续的两个长度为subLength的链表
            while curr:
                # 获取第一个长度为subLength的链表
                head1 = curr
                for i in range(1, subLength):
                    if curr.next:
                        curr = curr.next
                    else:
                        break
                # 获取第二个长度为subLength的链表
                head2 = curr.next
                curr.next = None            # 第一个链表head1结尾置None
                curr = head2
                for i in range(1, subLength):
                    if curr and curr.next:
                        curr = curr.next
                    else:
                        break
                
                succ = None                 # 用来迭代获得下两个长度为subLength的链表
                if curr:
                    succ = curr.next
                    curr.next = None        # 第二个链表head2结尾置None
                
                merged = merge(head1, head2)
                prev.next = merged          # 将新的子链表合并到原来的上面
                while prev.next:
                    prev = prev.next        # prev指向当前合并完的最后一个元素
                curr = succ                 # 更新curr以便下次遍历。
            subLength <<= 1                 # 子链表长度翻倍
        
        return dummyHead.next

时间复杂度O(logn),因为只用到了几个指针来记录位置,所以空间复杂度O(1)

155.最小栈

使用单调栈help_维护当前栈stack中的最小值,help_栈顶的元素即为stack中的最小值。需要注意的是,当最小值相等的情况也应该加入help_中,省去了计数的麻烦。

class MinStack:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []               # 存储当前栈中所有元素
        self.help_ = []               # 维护一个单调栈,栈顶元素为当前stack中最小值

    def push(self, x: int) -> None:
        self.stack.append(x)
        # 当出现更小元素的时候,加入辅助站help_中
        #(注意相等的时候也要加入,否则还要计数处理相等的情况)
        if not self.help_ or x <= self.help_[-1]:
            self.help_.append(x)

    def pop(self) -> None:
        num = self.stack.pop()
        # 如果弹出的元素正好是当前最小,则更新辅助站help_
        if num == self.help_[-1]:
            self.help_.pop()

    def top(self) -> int:
        return self.stack[-1]

    def getMin(self) -> int:
        # 辅助站栈顶元素即当前栈的最小值
        return self.help_[-1]


# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值