Table of Contents
- System Design
- Stack
- Queue
- Graph-based DSA
- Binary Search
- Binary Search Trees
- Math
- Array
- Hash Table
- Linked List
- Two-pointers
- Heap
- Dynamic Programming
看到 数组有序,就是二分,
问组合 + 数据size < 20 ,就是回溯,
问括号, 就是栈,
问图里的点的联通,就是UFS(DFS, BFS也可以,但是我超级喜欢UFS),
问有向图的依赖关系, 就是拓扑排序,
问最短距离,就是BFS,
问链表或者二叉树,递归或者迭代走起,
问TOPK, 就是堆,
题目暗示答案存在的可能区间,二分试探冲冲冲, ————————————————
Top Interview Questions (145 Questions)
- 主要思路:
- 知识点:
- 细节:
- 目前解法的cost
Brute-force
Levenshtein distance
- Space complexity: output space that is simply used to return the output (and not to do any processing) is not counted for the purpose of space complexity analysis
- Python
yield
is a keyword that is used like return, except the function will return a generator.from collections import OrderedDict
,a = OrderedDict()
,a.popitem(last=True)
: The popitem() method allows you to remove and return an item from either the end or the beginning of the underlying ordered dictionary. The pairs are returned in LIFO order if last is true or FIFO order if false.a.move_to_end(key, last=True)
: Move an existing key to either end of an ordered dictionary. The item is moved to the right end if last is true (the default) or to the beginning if last is false. Raises KeyError if the key does not exist.- When we iterate over an OrderedDict, items are returned in the order they were inserted.
- update new key-value pair, need
a.move_to_end(key)
, thena[key]=value
2. Add two sums (medium)
- 主要思路: create a parent linked-list, and a child/dummy linked-list, dummy linked-list is moving ahead to update the linked-list, which will affect the parent linked-list
- 知识点: 1. Understand how linked-list works in Python <=> understand Python is a pass-by-object-reference language, not pass-by-reference 2. use “carry” to carry module and add remainder to parent linked-list by updating dummy linked-list 3. learn how to use “%” (divider), and “//” (remainder) syntax
- 细节:1. if
l1==None
,l1.val
&l1.next
not exist becuz “Nonetype doesn’t have a value/next”. Need to consider if-else to counter this case 2. what is the stopping criterion for the loop 3. how to update the linked-list, especially whenl.next==None
.
739. Daily Temperatures
- 主要思路: first in last out. stack 中存储item 的index, 如果stack为空或者新的item大于stack里的last item, 将last item index = stack.pop(), 在last item 对应的index放上(current item index-last item index), then append current item into stack
1047. Remove All Adjacent Duplicates In String
- 主要思路: simple stack
496. Next Greater Element I
- 主要思路: use stack to go over num2, find next greater numbers, store in hashmap (用hashmap的原因是all nums in nums1 also appear in nums2). then use hashmap for nums1 to find output.
394. Decode String
- 主要思路:
- 如果是数字: 将数字字符化成整数, 存入multi
- 如果是字符: 延长当前字符串res
- 如果是左括号: 当前状态(multiple, res)入stack
- 如果是右括号: 弹出stack.pop()与当前字符串res组合
224. Basic Calculator
- 主要思路:
- compute the parenthesis level of each operator (从左到右扫一遍, 遇到左括号就加一)
- use stack to maintain the execution order of operators
- use a separate stack to maintain operands
84, 42, 901
Dynamic Programming
139. Word Break
- 主要思路: 设一个length为len(word)+1的全False的list 叫dp, +1这边指空集, set 0位为T dp[0] = True, 然后一位一位往后移, if dp[i], then 尝试将word in the word Dict 放进去 (for word in wordDict, if s[i: i+len(word)]==word, 如果放的进去,那么对应位置的index 为True dp[i+len(word)] = True. 最后看最后一位是不是为T, if T, then this word can be merged from items in word Dict.
5. Longest Palindromic Substring
42. Trapping Rain Water
- 主要思路: 先扫一遍得到最大高度且记录其index,再扫一遍, 看每个vertical 条的蓄水量(而不是考虑整体面积)。 从左到右到最大高度的index, if current height>left_most: update left_most, if <: 加入蓄水单位(left_most-current_height). 类似的从右到左到最大高度的index, if current_height >right_most: update right_most, if <: 加入蓄水量
List
16. 3Sum Closest
- 主要思路: two-pointer
- 类似题: #15
215. Kth Largest Element in an Array
- 主要思路: 设置pivot, left array (collect all nums that is greater than pivot), mid array (equal), right array (less than),and if k<=Len(left), 那么数在left array 里, do it recursively, if k > len(left)+len(mid), 那么数在right array 里, call self.(right, k-(L+M)), 因为left and mid已经占位L+M, 所以剩下k-(L+M). else, 那么数在mid array 里, 只要return mid[0], mid里的任何一个数即可
46. Permutations
- 主要思路: recursion. for i in range(len(nums)): n= nums除了i位的其他数字, 然后self.permutate(n)
238. Product of Array Except Self
- 主要思路:先从左往右过一遍, res[i] = res[i-1]nums[i-1], res[0] = 1 (此时res[i]存的事product(j=0, .. i-1)), 再从右往左过一遍, nums[i] = right nums[i-1], right *= nums[i] (right最开始为1, right记录的是i+1, .., end的总乘)(这样一来res[i]存的是0,..,i-1的总乘再乘i+1, .. end的总乘在。
- 注意: 要能O(1) space: 讲解https://www.youtube.com/watch?v=rpQhKorJRd8
33. Search in Rotated Sorted Array
- 主要思路: binary search, left, right = 0, len(nums)-1, mid = (left+right)//2, 然后分情况
- 情况1: target >= nums[0]
- 情况1a): nums[mid] >= nums[0] (0-mid都是ascending), and target>nums[mid], then 将left移到mid+1
- 情况1b): nums[mid] < nums[0] (0-mid中间有pivot, 但因为target还是比nums[0]大,所以移动right) 或者 target<=nums[mid], then 将right 移到mid-1
- 情况2: target < nums[0]
- 情况2a): nums[mid] >= nums[0] (0-mid都是ascending), OR target>nums[mid], then 将left移到mid+1
- 情况2b): nums[mid] < nums[0] (0-mid中间有pivot, 但因为target还是比nums[0]大,所以移动right) 或者 target<=nums[mid], then 将right 移到mid-1
- 情况1: target >= nums[0]
31. Next Permutation
- 题意: 将list里的数重新排列,得到下一个比当前int大的数, 如果找不到的话, 重新排列到ascending order
- 主要思路: 从右往左,看是否是ascending, 如果全是,则重新排列(从左到右做ascending), 如果有一位j是小于前一位的j+1,则从j+1,..end找最接近j的digit设为i位置, swap i with j, 然后再将j+1..end做ascending排列。
- reverse a list,
i=0, j=end, while i<j: swap(nums, i, j)
4. Median of Two Sorted Arrays (hard)
- 主要思路: $A\cup B$的中位数的左边一定是A左边贡献了几个数+B左边贡献了几个数, 找到短array的分割线==找到长array的分割线(因为长array的分割线=长加短长度//2-短分割线的长度), 对短array做binary search去找分割线(终止条件是所有A, B在分割线左边的数小于或等于右边的数)。
- 讲解: https://www.youtube.com/watch?v=ScCg9v921ns
36. Valid Sudoku (Easy)
- 主要思路:hash map
String
3. Longest Substring Without Repeating Characters (medium)
- 主要思路:sliding window, two-pointer
- 知识点:sliding window
- 细节: 1. time and space complexity, 2. add element to set
set.add(1)
, it is in-place addition
811. Subdomain Visit Count
- 主要思路: hashmap,
str.split("")
560. Subarray Sum Equals K
- 主要思路: hashmap, 创造一个prefix hashmap, key为pre[i]的value, value为pre[i]出现的次数(解释:pre[i] = sum(j=0, i) num, pre[j-1]-pre[i] = j-1位置和i位置之间所有数的和, 那我们只要看if pre[i]-k (i.e., pre[j-1])出现在prefix hashmap中即可). for num in nums, 先算pre[i], 再算pre[i]-k, check if pre[i]-k in hashmap, yes output+=1 (yes说明什么?说明从current index i到j: pre[i]-k的数值的j之间的数相加为k), then add pre[i] to hashmap.
- hashmap in Python is just dict.
102: Binary Tree Level Order Traversal
- Question: given a binary tree, read its values from top to bottom, return as list of lists.
- 主要思路:
- from collections import deque, deque in more memory sufficient in append and pop O(1) than list O(n)
- queue is root
queue=deque([root])
, then while queue, n = len(queue), and for i in range(n), node=que.popleft() the most left value. append node.left/.right to queue if they are not null, append node.value to current_level list, finish i loop, append current_level list to output list. then go back to while queue loop.
- Similar questions: Q993 Cousins in Binary Tree (Two nodes of a binary tree are cousins if they have the same depth, but have different parents).
95: Binary Tree Inorder Traversal
- 主要思路:
- what is in-order traversal
- create a queue to record current path, and a visited set to record all visited nodes. node is current node == last item in the queue. If node has left child node, and this node is not in visited, add it to queue and to visited. And update no_left flag to False. If no_left flag is true, append current node value to output, and delete node from queue. Inside this if, if node.right exists and doesn’t exist in visited, append right node to queue and visited.
75: Sort colors
- 主要思路: three pointer
- start, mid, end as three pointers. start <= mid <= end. swap mid and start if current value is 0. (swap is either no change to nums, or makes the order correct for nums[mid] and nums[start]). increment mid if current value is 1. Swap mid and end if current value is 2 (no change in the case of both are 2, or gain, current value is 2 and end is not 2), decrease end index.
763. Partition Labels
- 读题: partition string使得每个letter出现在最多一个其中的一个子串里。
- 主要思路: hashmap. 扫一遍string, hash[character]=他能走到的最远距离index. 再扫一边, 做分割 (如何做: 记录last=max(last,hash[character]), 如果last==i (等于当前index), 做分割, 加入result list, reset start pointer).
11. Container With Most Water
- 主要思路:双向two-pointer(两个pointer一个在头一个在尾), 跟新area, 移动left pointer if
left height<right height
(原理: If we try to move the pointer at the longer line inwards, we won’t gain any increase in area, since it is limited by the shorter line. ), wise versa.
1041. Robot Bounded In Circle
- 解题思路: x,y表示位移, move=[(0,1), (-1,0), (0,-1), (0,1)] direction的值0,1,2,3表示北,西,南,东,同时也是move的index。 遍历instructions, 对应每一个指令update x,y,direction, 最后判断是否x,y=0, or direction!=0(因为起始是面向北的).
- 最后direction!=0是为了防止GLRG这种情况,这种情况下就是zigzag越走越远。
Heap
56: Merge intervals
- 主要思路:sorted(list, key=lambda i: i[0])
- 细节:given a list of lists (each list item is an interval), first sort items by their left boundaries in an ascending order. create an empty list
output
. If output’s last item’s right boundary is smaller or equal to current’s right boundary, then merge, otherwise, save current into output. - 相似问题: 456 Non-overlapping Intervals
intervals = sorted(intervals)
res = [intervals[0]]
rightmost = intervals[0][1]
for i in intervals[1:]:
if i[0]<=rightmost<= i[1] or rightmost >= i[1]:
popInt = res.pop()
res.append([min(popInt[0], i[0]), max(popInt[1], i[1])])
rightmost = max(popInt[1], i[1])
else:
res.append(i)
rightmost = i[1]
return res
LinkedList
160. Intersection of two linked list
- 主要思路:分别走两个linked list,直到一方为空,则下一步连到另一条的开头。这样一来,总有一个点两个linked list都会走到空或者走到intersection(两个linked list走到空或者走到intersection时走的总步数时一样的)。则结束。
- node 为null,则
not node
is false. - 讲解: https://www.youtube.com/watch?v=obNV22YH1Nk
Trees
199. Binary Tree Right Side View
- 用DFS解法: root -> right -> left先访问右节点再左节点,记录每个节点的value and level, 将进入新的level的第一个节点加入我们的结果list里
797. All Paths From Source to Target, 17. Letter Combinations of a Phone Number
- 这两题用一模一样的方法
- 用DFS解法, and backtracking(用同一个path variable to go over all nodes)
- 细节:必须要
res.append(list(path))
res = [] path = [1,2,3] res.append(path) print(res) => [[1,2,3]] path.pop() print(res) => [[1,2]]
res = []
path = [1,2,3]
res.append(list(path))
print(res) => [[1,2,3]]
path.pop()
print(res) => [[1,2,3]]
22. Generate Parentheses
- DFS+ backtracking
- 主要思路: 用n=3画图, define a helper function, if right == n: append curr to res, return res, if left < n: self.helper(curr+”(“, left+1, right): 表示还有 机会加left bracket, if right < left, self.helper(curr+”)”, left, right+1): 表示还有机会加right bracket.
235. Lowest Common Ancestor of a Binary Search Tree
- 读题: 这个node可以是自己的ascendent, 这题assume每一个node is unique.
- 利用BST的data structure
- 思路: 用pointer判断目前在哪个node. 判断p.val 和pointer.val和q.val的关系,决定要不要移pointer
236. Lowest Common Ancestor of a Binary Tree
- 读题: 这题和235的区别是因为data structure的结构不同,所以左右都得搜寻,搜寻时如果不包含p/q, 返回null, otherwise返回包含的值(p/q) 只有当一个node其左右children都返回值而不是返回null, 这个node为最后的结果
- recursion: if root is null or root is p/q, return root, then resursion root.left and root.right, if root.left and root.right are not null, return this root, else return not null side, i.e., root.left or root.right.
116. Populating Next Right Pointers in Each Node
- 读题: 关于 PBT
- 解题思路: 如果root不是leaf node, then do the following, connect root.left with root.right (就是例子里connect 4 and 5, connect 6 and 7, and if root.next, connect root.right.next with root.next.left,(就是例子里, connect 5 and 6). )if root is leaf node, return root.
105. Construct Binary Tree from Preorder and Inorder Traversal
- 思路: 了解preorder, inorder。 preorder的第一个值一定是这个tree的root node 我们就可以create 一个TreeNode root。 利用这个root node找到其在inorder里的位置,其位置的左边的所有node一定是这个root node 的left nodes, 其位置右边的是所有的right nodes. Do it recursively, create root.left 的这个node, 用的是preorder[root的下一个index: 在inorder中root的位置+1(+1 因为right end is exclusive)], inorder[0: 在inorder中root的位置+1]. Recursive until preorder or inorder is none, then return Null (None node).
543. Diameter of Binary Tree (Easy)
- 读题: 算最长path,而不是经过的node
-
Diameter也可看作的树的height
if root is None: return self.res self.calculateHeight(root) return self.res
def calculateHeight(self, root): if root is None: return 0 l = self.calculateHeight(root.left) r = self.calculateHeight(root.right) self.res = max(self.res, l+r) # update res, root node as turning point return 1+max(l,r) # return height of this root node
================================================
System-Design
================================================
146. LRU Cache (Medium, 常考)
- orderedDict
================================================
Stack
================================================
- LIFO (注意与Queue的区别)
- For the array-based implementation of a stack, the push and pop operations take constant time, i.e. O(1).
- 写stack的时候需要考虑的点
- 一定有一个append的condition和remove的condition, 把这两个原则想清楚
-
if stack
的情况怎么办
232. Implement Queue using Stacks (Easy Design, 相似题: 225)
- 目前解法: 设置两个stack, stack 1, 存1,2,3,4, stack2 存4,3,2,1
- complexity:
- append: O(1)
- pop: worst case O(N), need to put everything in stack 1 to stack2 in backwards, after stack is there, pop is O(1)
- peek: O(1)
- isEmpty: O(1)
844. Backspace String Compare (Easy, 相似题: 71)
- 目前解法: time O(M+N), space: O(M+N), M, N are lengths of s, t respectively
- 相似题:
-
- Baseball Game (Easy): definition of stack
-
- Simplify Path (Medium, FB常考)
-
636. Exclusive Time of Functions (Medium)
- 主要思路: 将下一个start 看作上一个start的end.
- 目前解法: time: O(N), space: O(d): d is number of function calls
1249. Minimum Remove to Make Valid Parentheses (Medium, FB常考)
- 主要思路: stack 存(, idx_to_remove 存多的), 第一遍construct idx_to_remove=idx_to_remove union stack, 第二遍用construct new string
- 目前解法: time: O(N), space: O(N)
921. Minimum Add to Make Parentheses Valid (Medium, 相似题: 856)
- Keep track of the balance of the string: No.”(“ - No. “)”. A string is valid if its balance is 0, plus every prefix has non-negative balance. The above idea is common with matching brackets problems, but could be difficult to find if you haven’t seen it before. Now, consider the balance of every prefix of S. If it is ever negative (say, -1), we must add a ‘(‘ bracket. Also, if the balance of S is positive (say, +B), we must add B ‘)’ brackets at the end.
- 目前solution: time O(N): length of S, space O(1)
- 相似题:
-
- Score of Parentheses (Medium, Google 常考): Our goal is to maintain the score at the current depth we are on. When we see an opening bracket, we increase our depth, and our score at the new depth is 0. When we see a closing bracket, we add twice the score of the previous deeper part - except when counting (), which has a score of 1. Time, space = O(N)
-
- Minimum Insertions to Balance a Parentheses String ``` res represents the number of left/right parentheses already added. right represents the number of right parentheses needed.
-
1) case ) If we meet a right parentheses , right–. If right < 0, we need to add a left parentheses before it. Then we update right += 2 and res++ This part is easy and normal.
2) case ( If we meet a left parentheses, we check if we have odd number ‘)’ before. If we right, we have odd ‘)’ before, but we want right parentheses in paires. So add one ‘)’ here, then update right–; res++;. Note that this part is not necessary if two consecutive right parenthesis not required.
Because we have ), we update right += 2.
### 1209. Remove All Adjacent Duplicates in String II (Medium, FB常考)
* 主要思路: stack存[letter, frequency]
* 目前解法: time O(N), space: O(N)
================================================
## Queue
================================================
* FIFO
Terminology:
* enqueue: put items in the queue.
* dequeue: remove items from the queue.
Basic Operations of Queue
* enqueue
* dequeue
* isEmpty: check if queue is empty
* isFull: check if queue if full
* Peek: get value of the front of the queue without removing it
Working of Queue
* Queue operations work as follows:
* two pointers FRONT and REAR
* FRONT track the first element of the queue
* REAR track the last element of the queue
* initially, set FRONT and REAR = -1
1. Enqueue Operation
* check if the queue is full
* for the first element, set the value of FRONT to 0
* increase the REAR index by 1
* add the new element in the position pointed to by REAR
2. Dequeue Operation
* check if the queue is empty
* return the value pointed by FRONT
* increase the FRONT index by 1
* for the last element, reset the values of FRONT and REAR to -1
Applications of Queue
* CPU scheduling, Disk Scheduling
* When data is transferred asynchronously between two processes.The queue is used for synchronization. For example: IO Buffers, pipes, file IO, etc
* Handling of interrupts in real-time systems.
* Call Center phone systems use Queues to hold people calling them in order.
Complexity Analysis
* Enqueue and dequeue operations in a queue using an array is O(1)
## Circular Queue
* the circular increment is performed by modulo division with the queue size.
Circular Queue Operations
* The circular queue work as follows:
* two pointers FRONT and REAR
* FRONT track the first element of the queue
* REAR track the last elements of the queue
* initially, set value of FRONT and REAR to -1
1. Enqueue Operation (会动的只有REAR)
* check if the queue is full, if yes, return False
* elif: for the first element, set value of FRONT to 0
* else: circularly increase the REAR index by 1 (i.e. if the rear reaches the end, next it would be at the start of the queue), add the new element in the position pointed to by REAR
2. Dequeue Operation (会动的只有FRONT)
* check if the queue is empty, if yes, return False
* elif at last item (FRONT == REAR), return queue[FRONT] and reset FRONT, REAR to -1
* else: return the value pointed by FRONT, circularly increase the FRONT index by 1
3. Check if Queue is Full
* Case 1: FRONT = 0 && REAR == SIZE - 1
* Case 2: FRONT = REAR + 1
## Priority Queue
* In a queue, the first-in-first-out rule is implemented whereas, in a priority queue, the values are removed on the basis of priority. The element with the highest priority is removed first.
* we can use linked-list, binary search, heap to implement priority queue.
-----------------------------------------------
### 622. Design Circular Queue (Medium, Circular Queue)
* use circular definition above
* 相似题:
* 641. Design Circular Deque (Medium): 完全和622一样的想法
### 767. Reorganize String (Medium, Priority Queue)
* 解题思路: alternating inserting the most common letter to res.
* 相似题:
* 621. Task Scheduler (Medium)
================================================
## Binary-Search
================================================
* Binary Search is a searching algorithm for finding an element's position in a sorted array.
* Binary search can be implemented **only on a sorted list of items**. If the elements are not sorted already, we need to sort them first.
* Time Complexities: b
* best case complexity: O(1)
* Average case complexity: O(log n): proof: The question is, how many times can you divide N by 2 until you have 1? This is essentially saying, do a binary search (half the elements) until you found it. In a formula this would be this: $1 = \frac{N}{2^x} \rightarrow x=\log(N)$
* Worst case complexity: O(log n)
* Space Complexity: O(1)
* template
left, right = 0, len(arr)-1 while left<right: mid = (left+right)//2 if arr[mid] < x: left = mid + 1 else: right = mid return left
其实最后left==right, return arr[left]: first value >= x
-----------------------------------------------
### 278. First Bad Version (Easy)
* set two pointers, start and end, check if mid is True, then move pointers accordingly.
* 相似题:
* 374. Guess Number Higher or Lower (Easy)
* 35. Search Insert Position (Easy)
* 34. Find First and Last Position of Element in Sorted Array (Medium, 常考)
### 34. Find First and Last Position of Element in Sorted Array (Medium, 常考)
* binary search to find left bound, then run the binary search to find right bound, so total is 2 binary search = 2*O(logn)
* time: O(logn), space O(1)
### 658. Find K Closest Elements (Medium)
* binary search to find x. Starting from x's index, expand window by comparing if which of the left/right item is closer to x, and include it to the window, run until sliding window size reach k.
* 细节: sliding window: left,right = bsearch_output-1, bsearch_output(why? because bsearch_output>=x)
================================================
## Binary-Search-Trees
================================================
* 问题:Given an array of numbers, interval $k$, given another number $m$问
1. 是否能将$m$ insert into the array $R$ with all numbers separated by at least distance $k$
2. can u do in $O(logn)$
* 几种可能解法:
1. sorted array:
* do binary search and find the place to insert: $O(log n)$
* compare against $R[i-1]$ and $R[i]$
* insert: $O(n)$, because it is an array, requires shifting elements
2. heap
* insert: $O(logn)$
* find the place to insert: $O(n)$
3. Dictionary or python set:
* insert: $O(1)$
* find the place to insert: $\Omega(n)$ (lower bound, at least this much)
* So 我们需要binary search trees
* 定义:it is a **data structure**. `node.left.val < node.value < node.right.val`
* 二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低。为 $O(\log n)$
* time complexity: All operations (search, insert, deletion) are O(h) where $h=log n$ is height of the BST.
* space complexity: $O(n)$
-----------------------------------------------
#### 501. Find Mode in Binary Search Tree (Easy)
* 相似题:
* 98. Validate Binary Search Tree (Medium)
* recursion+定义一个函数叫valid去做functioning的一些工作
* `valid(root, min, max)`: 因为树的左边必须都小于顶端root, 右边必须大于顶端root。
* 在同一个class下define function记得加self `def valid(self, root, min, max): `
### 1038. Binary Search Tree to Greater Sum Tree (Medium, 常考)
* recursion solution (or DFS), first right node, then modify node.val, then save self.prev(to save up-to-date sum), then do left node.
* time: O(N), space: O(1): in-place modify
* 相似题:
* 538. Convert BST to Greater Tree: same question
================================================
## Trees
================================================
## Binary Tree
* It's a **tree data structure** in which each node has **at most two children**.
* Depth First Traversals:
1
/ \
2 3 / \ 4 5 ``` * Inorder (Left, Root, Right) : 4 2 5 1 3 * Preorder (Root, Left, Right) : 1 2 4 5 3 * Postorder (Left, Right, Root) : 4 5 2 3 1 * Breadth First or Level Order Traversal : 1 2 3 4 5 -----------------------------------------------
Recursion:
509. Fibonacci Number
- momorize past results to cache +recursion
- time: O(N), space: O(N)
226. Invert Binary Tree (Easy, 相似题617)
- 先top-down到达最底部 再bottom-up, 边up边invert
- 这个节点no value, no left, no right, then
root is None
is true. - 相似题:
-
- Merge Two Binary Trees (Easy)
-
144. Binary Tree Preorder Traversal (Easy,相似题 94, 145)
- traversal definition
- 相似题
-
- Binary Tree Inorder Traversal (Easy)
-
- Binary Tree Postorder Traversal (Easy)
-
Stack (iterative)
589. N-ary Tree Preorder Traversal (Easy,相似题 144,94,145,590)
- 主要思路: 用stack存node, stack.pop()出current node,then add its value to result list.
- 相似题: 以下可以用stack做
-
- Binary Tree Preorder Traversal
-
- Binary Tree Inorder Traversal
-
- Binary Tree Postorder Traversal
-
DFS (Depth-first search)
199. Binary Tree Right Side View
- 用DFS解法: root -> right -> left先访问右节点再左节点,记录每个节点的value and level, 将进入新的level的第一个节点加入我们的结果list里
1448. Count Good Nodes in Binary Tree (Medium)
- constantly keep track of the current path maximum
================================================
Graph-based-DSA
================================================
Graph Terminology
- $V$: set of vertices, $E$: set of edges
- directed graph: $E={(a,b), (b,a)}$: a directs to b and vice versa
- undirected graph: $E={{a,b}, {b,c}}$: an edge b/t a and b , and b and c, but not b/t a and c
- Adjacency: A vertex is said to be adjacent to another vertex if there is an edge connecting them. Vertices 2 and 3 are not adjacent because there is no edge between them.
- Path: A sequence of edges that allows you to go from vertex A to vertex B is called a path. 0-1, 1-2 and 0-2 are paths from vertex 0 to vertex 2.
- BFS vs DFS
Compare | DFS | BFS |
---|---|---|
不同 | from s toward one outgoing edge, recursively visit until the bottom, then switch to another outgoing edge | move 1 step/edge from s |
相同 | both need to record visted vertices to avoid repeat | |
can be viewed as a tree | ||
time O(V+E) | time O(V+E) | |
Application | Maximum path | Shortest path |
some applications that need visit all graph |
================================================
Graph-based DSA: DFS
================================================
- careful not to repeat a vertex ``` parent = {s: None}
only see stuff reachable from vertex s
DFS-visit (V, Adj, s): for v in Adj [s]: if v not in parent: parent [v] = s DFS-visit (V, Adj, v) # recursively visit
visit the entire graph, useful even if the graph is disconnected, consists of several clusters
DFS (V, Adj): parent = { } for s in V: if s not in parent: parent [s] = None # if s never visited, recusively visit all vertices connected to s (the cluster) DFS-visit (V, Adj, s)
* time: O(V+E)
* outer loop: visit all vertices: O(V)
* inner loop: DFS-visit is called only once for one versit. And visit all outgoing edges from the vertex: O(E)
* Application I: Job Scheduling
* Given Directed Acylic Graph (DAG), where vertices represent tasks & edges represent dependencies, order tasks without violating dependencies
* Topological Sort: run DFS, output the reverse of finishing times of vertices
* During DFS, after visited a vertex, you put into a list, so your output shall be the reverse of this list
* Proof why this works?
* proof statement: For any edge (u, v) — u ordered before v, i.e., v finished before u, reverse the visited list can do the topological sort
* case 1: if $u$ is visited before $v$
* proof: u is visited before v. since we have the edge $u->v$, before u finishes, we will visit v. That implies v finishes before u.
* case 2: if $v$ is visited before $u$.
* proof: if it happens, means that there exists an edge of $v->u$. Plusing our premise $u-> v$ implies we have a cycle in the graph. Contradiction. So case 2 cannot happen.
------------------------------------------------
### 690. Employee Importance (Easy, 相似题)
* recursion + hashtable
* 相似题:
* 339. Nested List Weight Sum (Medium): recursion
* 364. Nested List Weight Sum II (Medium): I write a function to do DFS to find max depth, then use #339 to calculate weighted sum
### 695. Max Area of Island (Medium, 相似题)
* time: O(R*C), space O(R*C): R: no. rows, C: no. cols
* 相似题:
* 200. Number of Islands (Easy)
* 主要思路: DFS, 将每一个grid看成一个node, 每个node有四个branch(left, right, up, down), 我们要做的就是DFS去找到所有的为1的grid将其value 从1改为*, *指的是这一片小岛,使得下次for loop到的时候不会增加count
================================================
## Toplogical-Sort
================================================
* 使用条件: 只针对Directed acyclic(acyclic means not form of a cycle) graph (DAG),有向无环图。(i.e., 这个图的边必须是有方向的;图内无环。)
* DAG <=> Toplogical Sort
* 如果这个图不是 DAG,那么它是没有拓扑序的;
* 如果是 DAG,那么它至少有一个拓扑序;
* 反之,如果它存在一个拓扑序,那么这个图必定是 DGA.
* toplogical sort is not unique.
* e.g., 要修C1, C2再修C3, C4, 这边就有两个toplogical sort, 一个是先修C1再C2, 一个是先修C2再C1
* Definition:
* 入度:顶点的入度是指「指向该顶点的边」的数量;
* 出度:顶点的出度是指该顶点指向其他点的边的数量。
* We can view a topological sort of a graph as an ordering of its vertices along a horizontal line so that **all directed edges go from left to right**
* BFS Implementation Algorithm (推荐):
* Map: `<key = Vertex, value = 入度>`
* 我们把入度为 0 的那些顶点放入 queue 中,然后通过每次执行 queue 中的顶点,就可以让依赖这个被执行的顶点的那些点的 入度-1,如果有顶点的入度变成了 0,就可以放入 queue 了,直到 queue 为空。
* DFS Implementation Algorithm:
* time complexity O(V+E)
* Application:
* 课程排序
* 判断是不是DAG: 另一个是如果题目没有给这个图是 DAG 的条件的话,那么有可能是不存在可行解的,那怎么判断呢?很简单的一个方法就是比较一下最后结果中的顶点的个数和图中所有顶点的个数是否相等,或者加个计数器,如果不相等,说明就不存在有效解。所以这个算法也可以用来判断一个图是不是有向无环图。
* 比如 pom 依赖引入 jar 包时,大家有没有想过它是怎么导进来一些你并没有直接引入的 jar 包的?比如你并没有引入 aop 的 jar 包,但它自动出现了,这就是因为你导入的一些包是依赖于 aop 这个 jar 包的,那么 maven 就自动帮你导入了。
------------------------------------------------
### 207. Course Schedule (Medium, 常考)
* step1: graph processing:
* construct in_edges`<vertex: no. ingoing edges>`, and out_edges`<vertex: [list of outgoing edges]>`
* step 2: BFS impletation of toplogical sorting
* time $O(|V|+|E|)$
* 相似题:
* 210. Course Schedule II (Medium, 常考)
### 329. Longest Increasing Path in a Matrix (Hard, 常考, textIQ考了)
* step 1: graph processing: construct in-edges and out_edges
* step 2: put initial in_edges=0 vertices into queue
* step 3: run BFS: use longest to record current level, return longest as results.
* time: O(M*N+ 4MN (no. edges))= O(mn) space: O(M*N)
================================================
## Graph-based DSA: BFS
================================================
* Goal: reach all nodes reachable from a given vertex $s\in V$
* Start from one node $s$, look at all possible node you can reach, build level i > 0 from level i − 1 by trying all outgoing edges, but **ignoring vertices from previous levels**
* $O(V+E)$ time, kinda optimal as you need to at least go over all vertices and edges.
* Algorithm: time O(V+E)
BFS (V,Adj,s): level={s: 0} # 记录哪个node出现在哪个level parent = {s : None } # 记录node u的上一层的node是谁 i=1 frontier = [s] # current level i-1 nodes while frontier: next = [ ] # next level i nodes for u in frontier: for v in Adj [u]: # all nodes reachable by node u. if v not in level: level[v] = i #确保已经visited过的node不要再visit到 parent[v] = u next.append(v) # append possible nodes to go frontier = next i + =1
* LC template
visited = set() queue = deque() level = 1 queue.append(separator) while queue: node = queue.pop() if node == separator: level += 1 if queue: queue.append(separator) else: for e in Adj[node]: … queue.append(e)
* Another way to look at the graph is Tree. As this graph can also be represented as a tree.
* BFS的经典题: find the shortest path from vertex $v$ to $s$.
* take v, read its parent[v], read its parent[parent[v]], etc., until s (or None), then read backwards is the shortest path from s to v.
* for every vertex v, fewest edges to get from s to v is either level[v] if v assigned level or inf (no path)
* 主要解题思路:
1. 用deque记录visited过的nodes, while deque, reach to next node from current node. Update level.
------------------------------------------------
### 994. Rotting Oranges (Medium, modify in-place)
* first create a deque to store rotten oranges positions. Use (-1, -1) to mark it's the end of a level. While queue, determine if need to add another level marker, and update rotten
* Time: O(N): N is the size of grid, enumerate it once to find all rotten oranges at state 0 (O(N)), then enumerate it once to update rotten oranges O(N), in total is O(N)+O(N) = O(N), space O(N): deque
* 相似题:
* 286. Walls and Gates (Medium)
* ThoughtExchange面试考了, 这题和rotting oranges一样的解法
* 130. Surrounded Regions (Medium)
* 主要思路: mark connected to border cells to "E", "E" means finally we want to escape flipping on these cells
* time: O(N), space: O(N): N no. of cells in the board
* 200. Number of Islands (Easy)
* 主要思路: DFS, 将每一个grid看成一个node, 每个node有四个branch(left, right, up, down), 我们要做的就是DFS去找到所有的为1的grid将其value 从1改为*, *指的是这一片小岛,使得下次for loop到的时候不会增加count
* 1197. Minimum Knight Moves (Medium)
* 典型BFS
* 542. 01 Matrix (Medium)
### 127. Word Ladder (Hard)
* 这道题的难点就是怎么找出dist(w1, w2). 这里用的方法是先过一遍wordList, 将wordList里每个word的pattern找出来, 存入dictionary。 key is pattern, value of a list of words that satisfy this pattern. E.g., pattern of a word "hit" are "*it", "h*t", "hi*".
* 细节: create a dictionary of list values `from collections import defaultdict, defaultdict(list)`
* time: total is $O(M^2N)$.
* calculate dictionary: $O(M*M*N)$. M is length of each word in wordList, N is len of wordList. we needs to create $M*N$ variants, each variant is created from substring operation, which takes $O(M)$.
* BFS: $O(M^2N)$: worst case is to go over all N words in wordList, and each word has M variants, and each variant is created from **substring operation**, which **takes $O(M)$ time**.
* space : $O(M^2N)$ (from $O(M*M*N)+O(MN)+O(MN)$)
* pattern dict $O(M*M*N)$: each word has M variants, and each variant has length M, in total we have N words
* Visited queue need $O(MN)$: N words, each word has length M
* queue for BFS $O(MN)$: worst case need to store all (N-1) words
* 相似题:
* 126. Word Ladder II (Hard): 记住要设置visited_at_this_level, and set break once find the shortest path to prevent exceed time limit
### 1091. Shortest Path in Binary Matrix (Medium)
* 也是需要replace cell's value to its level in BFS tree. similar to Q329.
* time O(MN), space O(MN), where M, N is no.rows, cols in the grid.
================================================
## Math
================================================
### 645. Set Mismatch (Easy)
* **list不能相减, set可以**
### 621. Task Scheduler (Medium)
* 类似于种树问题, 相同的task间至少要隔n个位置
* 找出freq最多的task, 那么结果是 max(len(tasks), (maxfreq-1)*(n+1)+nmax), where maxfreq是最多freq的task的freq, nmax:有几种最多freq的task。 e.g., n=2, A, _, _, A, _, _, A, 那么需要 2个 A, _, _ 组(长度为(maxfreq-1)*(n+1))加上最后的A。
* 细节: `ord('B')-ord('A')=1`, `<arr>.count(<element>)`: returns the number of times the specified element appears in the list.
* time: O(n): 因为要建这个freq array, space: O(1), 这个freq array 长度固定为26, 因为26个字母。
### 204. Count Primes (Easy)
* 主要思路: outer loop from 2 to $\sqrt{n}$ (because say $n = p\times q$, where $p \leq q$, then worst case is $p=q=\sqrt{n}$), inner loop, save multiples into a dict `multiples`.
* time complexity $O(\sqrt{n}l\log\log n)$, as $\sum_{primes\ i\ before\ n} \frac{n}{i} = O(loglog n)$ space : $O(n)$.
### 628. Maximum Product of Three Numbers (Easy)
* sort array first, 最大的是either last three items multiplication or nums[0]*nums[1]*nums[-1]
### 1822. Sign of the Product of an Array (Easy)
### 836. Rectangle Overlap (Easy)
### 592. Fraction Addition and Subtraction (Medium)
* `ints = map(int, re.findall('[+-]?\d+', expression))` get expression里的分数 (it is an iterable, move iterable to next item by `b = next(ints)`, read items in iterable by `for a in ints`),存做$\frac{a}{b}$, 将目前的sum叫做$\frac{A}{B}$. $\frac{A}{B}+\frac{a}{b}=\frac{Ab+Ba}{Bb}$, 算出分母分子的gcd by `math.gcd()`, updating 分母分子by `//gcd`
* re:
* `\d`: match any digit
* `(?...)`: The first character after the '?' determines what the meaning and further syntax of the construct is.
* `[]`: indicate a set of characters
* `+`: Causes the resulting RE to match 1 or more repetitions of the preceding RE. ab+ will match ‘a’ followed by any non-zero number of ‘b’s; it will not match just ‘a’.
* `map(int, <str expr>)`: map string expression to integer.
================================================
## Array
================================================
### 41. First Missing Positive (Hard, 常考)
* 思路和之前的一题一样, index sorting, nums[num] *= -1, 这样positive的index都会变负数。
* 类似题:
* 448. Find All Numbers Disappeared in an Array (Easy)
* 主要思路:
* 方法1是用set做, complexity is O(N). set() is essentially iterating over the list and adding each of those elements to a hash table.
* 方法2: in-place modify. 将数字对应在1-n的index变成-数字。 e.g.,[2,2], 2 corresponds to index 1 in 1-n, so mark 2 to -2. then iterate over the array again to find which index is non-negative, those are results.
* time: O(N)
* space: O(1): in-place
### 1200. Minimum Absolute Difference (Easy)
* 主要思路: minimum diff一定是在相邻的两个数之间, 所以先sort array
* time:
* sort: O(nlogn)
* go over array : O(n)
* space: O(N): worst case is that all numbers are equally spaced, thus store all pairs in, thus 2N numbers.
#### 1480. Running Sum of 1d Array (Easy, Amazon常考)
* 主要思路:过一遍, `nums[i] += nums[i-1]`
* time: O(N), space O(1)
### 163. Missing Ranges
* time: O(N), space O(1)
* The output list has a worst case size of O(N). This case occurs when we have a missing range between each of the consecutive elements in the input array (for example, if the input array contains all even numbers between lower and upper). We aren't using any other additional space, beyond fixed-sized constants that don't grow with the size of the input.
* However, **output space that is simply used to return the output (and not to do any processing) is not counted for the purpose of space complexity analysis**. For this reason, the overall space complexity is O(1).
================================================
## Hash-Table
================================================
### 266. Palindrome Permutation (Easy, 常考, 相似题)
* 判断a string 可否通过permutation得到一个palindrome => all chars appear even number of times, OR all char appear even number of times and except one appear once.
* time: O(N), space: O(1): because # of chars are bounded by 26.
* 相似题:
* 409. Longest Palindrome (Easy): 判断最长能组成palindrome的substr
* 这题两个情况: 对于出现次数为奇数的char,
* 情况1:第一次出现奇数, so `res+=freq`
* 情况2: 第二次出现, so `res+=freq-1`, 这样的好处,如果freq=1, 则不改变,如果freq>1, 则加入其偶数次freq。
* Lexicographically all Shortest Palindromic Substrings from a given string (Easy)
* shortest palindrome substring is just a single character.
* 注意: ord('a') == 97, create an array to keep track of alphabetic characters `abcd = [0]*26`
* time O(N), space O(1)
### 957. Prison Cells After N Days (Medium)
* 主要思路: 用hashmap记录循环
### 706. Design HashMap
* remove key from a dictionary `hashmap.remove('key', None)`
### 953. Verifying an Alien Dictionary
* 主要思路: create a hashmap for order, then two loops on words, one loop to loop over words, compare word with next word by looping over current word's index.
### 136. Single Number (Easy)
* `from collections import Counter, Counter(<a list nums>)= <hashmap>`
* 相似题:
* 389. Find the Difference (Easy)
* time O(N), space O(1): 因为这边最多就26个字母,所以不是O(N)而是O(1)
### 1772. Sort Features by Popularity (Medium)
* `sorted(l, key=, reverse=False)`: by default, sort in ascending order
* sort by multiple keys `key=(key1, key2, ...)`
* 巧妙用 `set` to reduce redundent appearance, 因为feature appears in the same response multiple times only counted as once.
### 1570. Dot Product of Two Sparse Vectors (Medium, FB常考)
* 注意: 这里bruce-force的做法是直接算,但这样一来time O(n) for both constructing sparse vec and calculating dot product, O(1) for constructing the sparse vector as we simply save a reference to the input array and O(1) for calculating the dot product.
* hashmap做法: 将nonzero entries存入hashmap: time O(n) for constructing sparse vec, O(L) for calculating dot, where L is no.non-zeros in vec. Space O(L): for constructing, O(1) for calculating
================================================
## Set
================================================
* Python built-in set
#### 349. Intersection of Two Arrays (Easy)
================================================
## Linked List
================================================
很好的整理资料: https://dongxiaoran.com/algo/basic/iterativelist/
### 206 Reverse Linked List (Easy, 经典题,together with 234)
* 主要思路: create a res node NULL, let head be the current node in the list. while head, let `res.next, head.next, head = head,res.next, head.next`
| Round | res.next=head | head.next=res.next | head=head.next |
| ------|:-------------:| ------------------:|---------------:|
| 1 | res:None->1->2->3| res: None->1->None| head: 2->3|
| 2 | res:None->2->3| res: None->2->1->None| head: 3|
* 细节:
* create a dummy node NULL `ListNode(float(-inf))`
* multiple values assign to multiple variables: `cur.next, pre, cur = pre, cur, cur.next` 这一行代码实现了,三个变量的赋值,而且有个特点,变量之间是有重叠的,问题也就出现在这里了,再进一步解释,当执行赋值中的第二个赋值(pre = cur)时,其实第一个赋值并没有生效,还是保持这行语句之前的状态,同样的执行赋值语句(cur = cur.next)时,同一行的两个赋值也并未生效,在这个代码背景中,就出现了节点游离的状态。不是先第一个,再第二个,再第三个,后一个的赋值覆盖前一个。
### 92. Reverse Linked List II (Medium, 206变形题, 类似题:143)
* 与206不同的是要造一个result的替身dummy去跑, 永远让dummy去更新
### 148. Sort list (Medium, 234变形题, 类似题:143,876,21)
* 主要思路: divide and merge
* divide: 用快慢pointer,将linkedlist 由中间分开(和题234一样)
* 一开始slow, fast, prev都指向head, slow and fast跑,到停的时候, 示意`prev.next=None`, 此时head会随之改变
* recursive call sortedList(head)
* merge two sorted list, 将两部分按大小merge起来, create a dummy, while h1 and h2: merge
* LinkedList: create 一个替身tail, dummy=tail=ListNode(-1), 然后tail用来跑current node, tail.next连到的node也会体现在dummy里, dummy维持整个linkedlist
* 相似题:
* 876. Middle of the Linked List
* 21. Merge Two Sorted Lists
* 143. Reorder List: 注意
1. 这边merge的时候要及时delink, 否则merge会成环 (因为我们先用`after = dummy.next`去存储, 再merge的时候用`dummy.next = after`, 所以成环)
2. 在reverse(slow)的时候也会改变global variable slow.
================================================
## Two-pointers
================================================
According to mechanism and usage, I have roughly classified them into five categories:
* Old and new state: old, new = new, cur_result
* Slow and fast runner: slow-> fast->->
* 
* Left and right boundary: |left-> ... <-right|
* Pointer-1 and pointer-2 from two sequences: p1-> p2->
* Start and end of sliding window: |start-> ... end->|
-----------------------------------------------
## Left-Right Boundary
### 1. Two Sum, 167. Two Sum II - Input array is sorted, 1099. Two Sum Less Than K (Easy, 经典题)
* 主要思路: two-pointer
* 细节: sort list's idx by list's values. `sorted(range(len(l)), key=lambda k: l[k])`, `lambda arguments : expression`
* 类似题:
* 345. Reverse Vowels of a String (Easy)
* time: O(n)
* space: O(n)
### 15. 3Sum (Medium, 经典题)
* 主要思路: two-pointer
1. 先sort array
2. lock one pointer i, and do 2 sum with the other two pointers j, k. if i+j+k对应的和< target, move j to right. (因为锁定i, 如果目前和小于target, 则将j 右移, 因为array是ascending的,往右移数越大,使其更接近target,如果碰到duplicates, i.e., 目前j和j-1数一样 e.g., `while j<k and nums[j]==nums[j-1]` e.g., `while i>0 and nums[i]==nums[i-1]: continue`, 则skip因为我们在j-1时已经检查过了和) 如果i+j+k > target, move k to left
### 1711. Count Good Meals (Medium)
* 这题我自己做的可能做的不是很好,我用的是hashmap+two-pointer的做法
### 125. Valid Palindrome (Easy, 常考)
* python `<str>.isalnum()` check if all the characters in the text are alphanumeric.
* check is s[left] == s[right]
* time: O(N), space O(1)
### 680. Valid Palindrome II (Easy, 常考)
* outer two-pointer until hit `s[left] != s[right]`, then run inner two-pointer on (left+1, right) and (left, right+1) to check if inner is palindrome.
* time O(N), space O(1)
## Fast-Slow Runner (用于找中位数/判断是否有环)
### 234. Palindrome Linked List (Easy, 经典题, similar #9(str))
* 主要思路: 3步
* 第一步:findMid: 这边用到的是fast and slow pointers, fast每次走两步,slow每次一步, 先设一个dummy node, 从dummy node 开始走,until fast is none or fast.next is none. 这样奇数个的linked-list, slow就能走到mid, 偶数个的linkedlist, slow能走到mid的左边(1->2->3->4 走到2)
* 第二步 reverse mid 后面的那部分 利用#206
* 第三步, compare head first half with reversed second half
* 细节:palindrome definition (对称性)
* 9. Palindrome Number: 注意可以用string[::-1] to reverse a string
### 287. Find the Duplicate Number (Medium, 相似题: 142)
因为题意说有且只有一个duplicate, 则一定有环two-pointer 将找duplicate 的问题化为环问题,
1. 找到相遇点(乌龟每次走一步, 兔子走两步, 直到兔子赶上乌龟, 假设a = 不是环的长度, b+c为环的长度slow and faster pointer 在b点相遇因为slow pointer走了=a+b,fast pointer=a+b+c+b=a+2b+c => since 2S=F, 2(a+b) = a+2b+c=>a=c )
2. 找环的开头, 再设两个指针,这次两个指针每次都走1步, 一个指针初始在开头,一个初始在b, 他两最后相遇的地方就是环的起点(因为两个分别走了a步和c步,且a=c)
* 相似题: 142. Linked List Cycle II
## Queue (FRONT, REAR也是一种two-pointer, 也是一种sliding window)
### 346. Moving Average from Data Stream
* circular queue
* time O(1), space O(N): construct the queue
## Sliding-Window
pick an easier one first.
Constrained Subsequence Sum
Number of Substrings Containing All Three Characters
Count Number of Nice Subarrays
Replace the Substring for Balanced String
Max Consecutive Ones III
Binary Subarrays With Sum
Subarrays with K Different Integers
Fruit Into Baskets
Shortest Subarray with Sum at Least K
Minimum Size Subarray Sum
More Good Stack Problems
Here are some stack problems that impressed me.
Constrained Subsequence Sum
Minimum Cost Tree From Leaf Values
Sum of Subarray Minimums
Online Stock Span
Score of Parentheses
Next Greater Element II
Next Greater Element I
Largest Rectangle in Histogram
Trapping Rain Water
### 1438. Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit (Medium, Google)
* use two queues, one called `maxq` to store items in monotonically decreasing order, the other called `minq`, store items in monotonically increasing order.
* 我们的sliding window的右端点每次会往右一格,(i.e., add j-th position item to maxq and minq).
* 同时跟新maxq使得maxq只保留[i,j]之间比j-th position 大的数, 更新minq使得其只保留[i,j]之间比j-th position 小的数
* 将j-th position 加入maxq, minq
* 我们会看if [i,j]里的max num-minnum <=k 来决定是否需要移动左端点.
* 如果要移, 判断 (proof is below) 是否i-th position的数是[i,j]里最大的数,if yes, remove it from maxq. 判断i-th position 的数是[i,j]里最小的数,if yes, remove it from minq. i += 1
* Time: O(N), space: O(N)
* 很好的解答: https://leetcode.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/discuss/609771/JavaC%2B%2BPython-Deques-O(N)
### 209. Minimum Size Subarray Sum (Medium)
* 解法1: sort then two sum. time: O(nlogn)
* 解法2: sliding window. 类似#1048,一个指针j一直往右, move指针i指着最小的window使得sum of window >= target. time: O(N) space: O(1)
================================================
## Heap
================================================
* tree-based data structure, 用来出k closest最好, 是一种priority queue的implementation
* a heap is a specialized tree-based data structure which is essentially an almost complete tree that satisfies the heap property:
* in a max heap, for any given node C, if P is a parent node of C, then the value of P is >= to the value of C.
* In a min heap, the value of P is <= to the value of C.
* Heap in Python:
import heapq
H = [21,1,45,78,3,5]
Use heapify to rearrange the elements, O(n)
heapq.heapify(H) # min heap heapq.heapify(-H) # max heap print(H)
Add element O(log n)
heapq.heappush(H,8) print(H)
Remove element from the heap, O(log n)
heapq.heappop(H) print(H)
Replace an element, The heapreplace function always removes the smallest element of the heap and inserts the new incoming element at some place not fixed by any order.
heapq.heapreplace(H,6) print(H)
equivalent to sorted(iterable, key=key)[:n]
heapq.nsmallest(n, iterable, key=None)
H[0]: smallest element
* heapq 在python中的实现原理(假设我们在看minheap)
1. 第一个问题:如何由一个无序序列建立成一个heap?从无序序列的第 n/2 个元素 (即此无序序列对应的完全二叉树的最后一个非终端结点 )起 ,至第一个元素止,依次进行下沉(下沉指的是将其和其left and right node 比,选left,right中小的与其进行对调)
2. 第二个问题: 如何在输出heap.pop()之后,调整剩余元素,使之成为一个新的heap? 采用的方法叫“筛选”,当输出堆顶元素之后,就将堆中最后一个元素代替之;然后将根结点值与左、右子树的根结点值进行比较 ,并与其中小者进行交换;重复上述操作,直至叶子结点,将得到新的堆,称这个从堆顶至叶子的调整过程为“筛选”。
3. 第三个问题: 新添加元素和,如何调整堆?这个的方法正好与 下沉 相反,首先将新元素放置列表的最后,然后新元素与其父节点比较,若比父节点小,与父节点交换;重复过程直到比父节点大或到根节点。这个过程使得元素从底部不断上升,从下至上恢复堆的顺序,称为 上浮 。
* time complexity (Binary n-item (min) heap )
| Type | Average | Worst case |
| ------|:-------------:| :-------------:|
| Space | O(n) | O(n) |
| Search| O(n) | O(n) 还是要遍历 |
| Insert| O(1) | O(logn) like binary search, 因为要一直比较左右上浮|
| Find-min| O(1) min at the top|O(1) 因为min就在root node|
| Delete-min| O(1)|O(logn)输出以后要重新调整成为新的heap, 这个过程like binary search, 因为要一直比较左右下沉|
为什么Search is O(n)?
求搜寻数59, 依旧需要遍历所有的Leaf node
98
/
78 65
/ \ /
58 48 60 59
```
973: K Closest points to origin(Medium, 相似题 below)
- 解题思路: min heap, 建一个min heap, node value 存point 到0点的距离。
heapq.heappush(H, (dist, (pt)))
, will construct heap based on first entry in the tuple. 需要记住Python heapq的用法 - 相似问题:
-
- Find Nearest Point That Has the Same X or Y Coordinate (Easy)
-
- Kth Largest Element in an Array (Medium)
- nums=> -nums need to use
[-e for e in nums]
,-nums, -1*nums
WON’T work.
- nums=> -nums need to use
- Kth Largest Element in an Array (Medium)
-
- Top K Frequent Elements (Medium)
heapq.nlargest(k, count.keys(), key=count.get)
- Top K Frequent Elements (Medium)
-
- Sort Characters By Frequency (Medium)
-
- Kth Largest Element in a Stream. (Easy)
- 注意: 因为只需要Kth largest, 连续用
heapq.heappop(H)
直到heap size<= K, 并且return heap[0]就行, 因为heap[0]为smallest item in the heap, 同时也是Kth - 可以用
heapq.heapreplace(H, val)
to replace the smallest item in H with val,这样可以保证heap size<=K
- 注意: 因为只需要Kth largest, 连续用
- Kth Largest Element in a Stream. (Easy)
-
- Sort Features by Popularity (Medium)
-
- Top K Frequent Words (Medium)
-
- Kth Smallest Element in a Sorted Matrix (Medium): current solution 还是有点costy: space O(k), best solution 有space O(min(K, N))
-
- Last Stone Weight. Time O(NlogN): Converting an array into a Heap takes O(N) time (it isn’t actually sorting; it’s putting them into an order that allows us to get the maximums, each in O(logN) time).
-
253. Meeting Rooms II (Medium, 相似题 below)
- 解题思路: min heap 存the ending times when there is a meeting room available. 先sort使得所有的开始时间成ascending order, 这样一来我们只需要关注下一个interval的开始时间是否大于之前的最迟的ending时间
- 相似题:
- 252 Meeting Rooms I (Easy)
- 56: Merge intervals
================================================
Dynamic-Programming
================================================
647. Palindromic Substrings, 5. Longest Palindromic Substring
- 主要思路: 建一个2d的array dp (string s x string s), i 为row, j 为col, 对角线都为True, 每一个位置为bool (if s starts at index i up to index j is a palindrome. )
- 细节: 填空的顺序也值得注意和思考, 是应该一行行填还是有技巧的填
- 讲解: https://www.youtube.com/watch?v=ZnzvU03HtYk
- time: $O(n^2)$, space: $O(n^2)$
================================================
================================================
Sliding window
- Use case: any problem where it is asking for any of the following return values can use a sliding window: Minimum value, Maximum value, Longest value, Shortest value, K-sized value.
- Applied to what data type: strings, arrays, and even linked lists.
- Sliding window algorithm: 1. a starting pointer 2. a Set (in Python) or Object (in JS) or Hash table (in other languages) to check for duplicates, hash table look up time is O(1) 3. loop: loop declares rule on when to increment left/right pointer.
- Why need set? set is used to help determine where the left pointer shall locate so that the window contains no duplicates
- test example : “pwwkew”, max non-dup substring len = 3
- Time complexity: O(n): n is length of the data
- Space complexity: O(n)
Binary Search
- 一般log级别的查找, 自然会想到binary search
Tree
- Tree problems, usually use recursion method
- Recursion: 主要看base case是什么,类似数学induction, 一般都要写一个辅助函数,然后let it run recursively.
Perfect Binary Tree (PBF)
- It’s a binary tree in which all interior nodes have two children and all leaves have the same depth or same level.
Deque: collection method
Deque (Doubly Ended Queue) in Python is implemented using the module “collections“. Deque is preferred over list in the cases where we need quicker append and pop operations from both the ends of container, as deque provides an O(1) time complexity for append and pop operations as compared to list which provides O(n) time complexity.
Stack
A stack is a linear data structure that stores items in a Last-In/First-Out (LIFO) or First-In/Last-Out (FILO) manner. In stack, a new element is added at one end and an element is removed from that end only. The insert and delete operations are often called push and pop.
Dynamic Programming (化归法)
Dynamic programming is breaking down a problem into smaller sub-problems, solving each sub-problem and storing the solutions to each of these sub-problems in an array (or similar data structure) so each sub-problem is only calculated once.
Counter in Python
- As the source code shows, Counter is just a subclass of dict. Constructing it is O(n), because it has to iterate over the input, but operations on individual elements remain O(1).
Backtracking
- It’s a technique based on algorithm to solve problem. It uses recursive calling to find the solution by building a solution step by step increasing values with time. It removes the solutions that doesn’t give rise to the solution of the problem based on the constraints given to solve the problem.