155. Min Stack
设计一个栈,要求在常数时间复杂度取出最小值。原题
1 | MinStack minStack = new MinStack(); |
1 | class MinStack: |
232. Implement Queue using Stacks
使用两个栈实现一个队列。原题
1 | MyQueue queue = new MyQueue(); |
方法一:push-O(1), pop-O(N).
1 | class MyQueue: |
方法二:push-O(N), pop-O(1).
1 | class MyQueue: |
225. Implement Stack using Queues
使用队列实现栈。原题
方法一:两个队列,push-O(1), pop/top O(n). 思想在于q2就是一个临时队列,无论是是top还是pop都是保持q1剩一个元素,然后再交换q1, q2.
1 | class MyStack: |
1 | class MyStack: |
1 | class MyStack: |
295. Find Median from Data Stream
找出数据流中的中位数。原题
思路:使用两个堆,最大堆存储较小的数据,最小堆存储较大的数据。添加数字时,先添加到最大堆,然后最大堆返回一个最大的数字给最小堆,最后为了平衡,可能需要最小堆还给最大堆一个最小值,以保证最大堆的长度>=最小堆的长度。由于headpq是最小堆,所以使用取反实现最大堆。添加数字:Time-O(logn),取出中位数:Time-O(1)。
1 | Adding number 41 |
1 | import heapq as hq |
480. Sliding Window Median
滑动窗口中的中位数。这题基于295的思想,但是要比其难处理的多。
1 | Window position Median |
方法一:我自己的思路是到了最大堆最小堆,不知道如何删除左侧窗口废弃的元素。看了讨论区知道了,要把索引加进去,但是当一个废弃的索引, 不在堆首无法将其剔除时,就是影响两个堆的长度,那么从而影响中位数的判断。这是这段代码的核心。首先,不能够像295那样,通过两个堆的长度来平衡,因为堆中可能含有废弃的元素,这里使用的是懒删除。然后尽量保证hi 即最小堆的长度较大,当要删除一个数时,判断这个数是在最大堆还是最小堆。如果是在lo中,则从hi补齐一个元素到lo中,因为平衡两个堆时不考虑将来要被删除的元素。20~27行是这段代码能正常运作的核心。
1 | def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]: |
方法二:还是stefan的方法好,比赛能做出来起码。
1 | def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]: |
bisect.insort
的时间复杂度为O(n)。这里使用SortedList可以优化到O(logn)。
1 | def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]: |
535. Encode and Decode TinyURL
设计一个短链接。原题
1 | class Codec: |
方法二:使用字典保存。
1 | class Codec: |
707. Design Linked List
设计一个链表。原题
1 | MyLinkedList linkedList = new MyLinkedList(); |
1 | class ListNode: |
146. LRU Cache
1 | LRUCache cache = new LRUCache( 2 /* capacity */ ); |
OrderedDict
.
开始有两个testcase
没过,一个是{2: 3, 1: 1}
更新2时,和{1: 1, 2: 3}
更新2时,二者都可以使用先pop再更新的方式。remain的方式是从discuss学来的,之前还用len
或额外的size
来保存。
1 | class LRUCache: |
方法二:不使用OrderedDict
1 | class DLinkedNode: |
460. LFU Cache
设计一个LFU缓存。
方法一:简单地看了一下题解后,写了半个下午。需要考虑的地方确实很多。其它的地方都还好,关于min_freq
的更新处踩了一个坑,就是必须得是当前最小的空了以后,再更新它。为了兼容capacity为0的情况,在DLinkedList中添加了count变量。
1 | class DLinkedNode: |
1381. Design a Stack With Increment Operation
设计一个栈,实现一个将k个栈底元素全部增加val的方法。原题
1 | Input |
方法一:普通的方法不记录了,这里学到一个延迟增加的方法。因为值的增加只有在pop
的时候才会体现,所以维护了一个inc
用来累加增加的值。
1 | class CustomStack: |
208. Implement Trie (Prefix Tree)
实现一个单词查找树。原题
1 | Trie trie = new Trie(); |
方法一:我的常规节点方法。
1 | class TrieNode: |
方法二:reduce
的妙用。
1 | class Trie(object): |
380. Insert Delete GetRandom O(1)
设计一个类,插入删除,随机都是O(1)。原题
1 | // Init an empty set. |
方法一:解法的思想有点像删除链表,是一种覆盖的思想。主要是删除时用最后的元素将其覆盖,然后再将末尾的元素删除。
1 | class RandomizedSet: |
341. Flatten Nested List Iterator
扁平化一个嵌套的List迭代器,NestedInteger是一个类。原题
1 | Input: [[1,1],2,[1,1]] |
方法一:这里设计时考虑了,next之前可能不会调用hasNext。因为正常来说一个生成器就是这样的。
1 | #class NestedInteger: |
5525. 皇位继承顺序
啰里啰嗦一大堆,实际上就是让见一个家族图谱,然后先序遍历。
方法一:比赛的时候想到了N叉树,但是设计上没想好怎么通过family节点融合。所以直接用的defaultdict做的。
1 | class ThroneInheritance: |
方法二:使用多叉树模拟。
1 | class Person: |
432. All O`one Data Structure
实现一个数据结构使所有的操作在O(1)时间复杂度。某个Key计数+1或者-1,返回最多、最少的key其中的一个。
方法一:这道题有难度而且实现很复杂,我开始的时候想着以LFU的形式来解,写到最后发现一个问题就是,当一个key的计数变为0的时候,我没法维护一个min_freq来记录最小的计数。对我来说此题首先要打破常规,之前总是认为Node节点就装node,其实这里可以装上一整个key的集合。整体上实现一个双链表,每一个单节点里对应了某些key的集合。节点按照计数顺序增序排列,使用node_freq
字典来定位每个频率的节点。
1 | class DLinkedNode: |
1670. Design Front Middle Back Queue
设计一个可以从中间入队出队的队列。
1 | Input: |
方法一:比赛时用的双端队列,一开始想的3个,后来想2个就够了。
1 | class FrontMiddleBackQueue: |
方法二:整理一下方法一。
1 | class FrontMiddleBackQueue: |
方法三:竞赛中直接使用复杂度较高的方法也能过。
1 | class FrontMiddleBackQueue: |
1825. Finding MK Average
找到M滑动窗口数据流中去除前后K个最大最小值的平均数。
1 | ["MKAverage", "addElement", "addElement", "calculateMKAverage", "addElement", "calculateMKAverage", "addElement", "addElement", "addElement", "calculateMKAverage"] |
方法一:这题暴力居然过了。。10**5的数据集,暴力排序就过了。实际上比赛的时候想到了数据流中的中位数,想着用队列和堆来解决。但是堆不好解决删除的问题。此题应该用SortedList。这里不清楚python底层是用什么平衡树实现的。
1 | from sortedcontainers import SortedList |