A hash table is a data structure used to store vals, optionally, with corresponding values.
- A mapping object maps hashable values to arbitrary objects. Mappings are mutable objects. The only standard mapping type in python is
Dictionary. - A
dictvals are almost arbitrary values. Values that are not hashable (like lists, dictionaries or other mutable types) may not be used as vals. - A
setis an unordered collection with no duplicate elements. Curly braces or the set() function can be used to create sets. (empty set must use set())
| Operation | Time Complexity |
|---|---|
| Access | N/A |
| Search | O(1) |
| Insertion | O(1) |
| Deletion | O(1) |
Dictionary Operations
d[val] # Return the item of d with val val. Raises a valError if val is not in the map.
d[val] = value # Set d[val] to value.
del d[val] # Remove d[val] from d. Raises a valError if val is not in the map.
val in d # Return True if d has a val val, else False.
val not in d # Equivalent to not val in d.
d.clear() # Remove all items from the dictionary.
d.get(val[, default]) # Return the value for val if val is in the dictionary, else default.
d.keys() # Return a new view of the dictionary’s keys.
d.values() # Return a new view of the dictionary’s values.
d.items() # Return a new view of the dictionary’s items ((val, value) pairs).Useful functions
from collections import defaultdict, Counter
# A defaultdict is initialized with a function ("default factory") that takes no arguments and provides the default value for a nonexistent key.
dic = {} # build-in dictionary
dic['a'] # KeyError, no such key
dic = defaultdict(int) # using int() as default factory
dic['a'] # defaultdict(<class 'int'>, {'a': 0})
# A Counter is a dict subclass for counting hashable objects.
cnt = Counter(['red', 'blue', 'red', 'green', 'blue', 'blue'])
print(cnt) # Counter({'blue': 3, 'red': 2, 'green': 1})Leetcode Problems
A binary tree is either empty, or a root node together with a left binary tree and a right binary tree.
Height Depth Level
__A__ ----> 4 0 1
/ \
__B C ----> 3 1 2
/ \ / \
D E F G ----> 2 2 3
/ \
H I ----> 1 3 4
- Node
Ais Root - Node
Ahas 2 children: NodeBand NodeC - Node
Bis NodeA's left child - Node
Cis NodeA's right child - Node
Ais NodeBand NodeC's parent - Node
Hand NodeIare a Leaf Nodes - Node
Ais NodeH's ancestor - Node
Iis NodeA's decedent
Binary tree
A tree has a root node and every node has at most 2 children
Full Binary Tree
A tree in which every node has either 0 or 2 children
Perfect Binary Tree
A full binary tree in which all leaves are at the same depth, and in which every parent has 2 children
Complete Binary Tree
A binary tree in which every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible.
- A complete binary tree has
2^knodes at every depthk < nand between2^nand2^n+1 - 1nodes altogether. - It can be efficiently implemented as an array, where a node at index
ihas children at indexes2iand2i+1and a parent at indexi/2, with 1-based indexing (2i+1and2i+2for 0-based indexing)
__A__
/ \
B C
/ \ / \
D E F G
/
H
[_,A,B,C,D,E,F,G,H]
Below are NOT Complete Binary Trees
__A__ __A__ ______A______
/ \ / \ / \
B C B C __B__ __C__
/ \ / \ / \ / \ / \
D E D E F G D E F G
/ \ \ \ \ / \ / \ / \ / \
F G H H I H I J K L M N O
BinaryTree Node
class BinaryTreeNode:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = rightBinary Tree Traversal
Pre-order Traversal: root -> left -> right
def preorder(self, root):
if root:
visit(root)
preorder(left)
preorder(right)In-order Traversal: left -> root -> right
def inorder(self, root):
if root:
inorder(left)
visit(root)
inorder(right)Post-order Traversal: left -> right -> root
def postorder(self, root):
if root:
postorder(left)
postorder(right)
visit(root)Level Order Traversal: top -> bottom, left -> right
def levelorder(root):
queue = collections.deque()
queue.append(root)
while queue:
node = queue.popleft()
self.visit(node)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)LeetCode Problems
- 144. Binary Tree Preorder Traversal
- 94. Binary Tree Inorder Traversal
- 145. Binary Tree Postorder Traversal
- 102. Binary Tree Level Order Traversal
- 589. N-ary Tree Preorder Traversal
- 590. N-ary Tree Postorder Traversal
- 429. N-ary Tree Level Order Traversal
A BST is a rooted binary tree whose internal nodes each store a val greater than all the vals in the node's left subtree and less than those in its right subtree.
| Operation | Time Complexity |
|---|---|
| Access | O(log n) |
| Search | O(log n) |
| Insertion | O(log n) |
| Deletion | O(log n) |
BST Template
def BST(root, target):
if root.val == target:
# found it, do something...
if val < root.val:
BST(root.left, val) # find in left tree
if val > root.val:
BST(root.right, val) # find in right treeSearch
def search(root, val):
if root is None:
return None
if val < root.val:
return self.search(root.left, val)
elif val > root.val:
return self.search(root.right, val)
return rootInsert
100 100
/ \ Insert(40) / \
20 500 ---------> 20 500
/ \ / \
10 30 10 30
\
40
def insert(root, val):
if root is None:
return BinaryTreeNode(val)
if val < root.val:
root.left = self.insert(root.left, val)
elif val > root.val:
root.right = self.insert(node.right, val)
return rootDelete
1) Node to be deleted is leaf: Simply remove from the tree.
50 50
/ \ Delete(20) / \
30 70 ---------> 30 70
/ \ / \ \ / \
20 40 60 80 40 60 80
2) Node to be deleted has only one child: Copy the child to the node and delete the child.
50 50
/ \ Delete(30) / \
30 70 ---------> 40 70
\ / \ / \
40 60 80 60 80
3) Node to be deleted has two children: Find inorder successor of the node.
Copy contents of the inorder successor to the node and delete the inorder successor.
50 60
/ \ Delete(50) / \
40 70 ---------> 40 70
/ \ \
60 80 80
def deleteNode(self, root: TreeNode, val: int) -> TreeNode:
if root is None:
return root
if val < root.val:
root.left = self.deleteNode(root.left, val)
elif val > root.val:
root.right = self.deleteNode(root.right, val)
else:
# has one child
if root.left is None: # only right child
return root.right
if root.right is None: # only left child
return root.left
# has two children
minNode = self.getMin(root.right)
root.val = minNode.val
root.right = self.deleteNode(root.right, minNode.val)
return root
def getMin(self, node: TreeNode) -> TreeNode:
# left most child is the smallest
while node.left is not None:
node = node.left
return nodeLeetCode Problems
- 100. Same Tree
- 700. Search in a Binary Search Tree
- 701. Insert into a Binary Search Tree
- 450. Delete Node in a BST
- 98. Validate Binary Search Tree
A heap is a complete binary tree, and is represent by array. The children of the node at index i are at indices 2i+1 and 2i+2.
Given element in a heap at position i:
- parent position:
(i-1) >> 1ori // 2 - left child position:
2*i + 1 - right child position:
2*i + 2
| Operation | Time Complexity |
|---|---|
| find-min | O(1) |
| delete-min | O(logn) |
| Insert | O(logn) |
| K largest | O(nlogK) |
| K smallest | O(nlogK) |
max-heap: the val at each node is at least as great as the vals at it's children.
_____561_____
/ \
___314_ _401_
/ \ / \
_28 156 359 271
/ \
11 3
min-heap: the val at each node is at least as small as the vals at it's children.
_____3____
/ \
_____11_ _28_
/ \ / \
_156_ 314 561 401
/ \
359 271
heapq Operations
heap = [] # creates an empty heap
heap[0] # smallest element on the heap without popping it
heapq.heapify(L) # transforms list into a heap, in-place, in linear time
heapq.heappush(h, e) # pushes a new element on the heap
heapq.heappop(h) # pops the smallest item from the heap
heapq.heappushpop(h, a) # pushes a on the heap and then pops and returns the smallest element
heapq.heapreplace(h, e) # pops and returns smallest item, and adds new item; the heap size is unchanged
heapq.nlargest(n, L) # Find the n largest elements in a dataset.
heapq.nsmallest(n, L) # Find the n smallest elements in a dataset.