Trees and Graphs
avl tree
a binary tree that, upon insertion, will rotate if the height of the left and right subtrees differ by more than |1| - stores height at each node if left-right case, do left rotation then right rotation. if right-left case, do right rotation then left rotation.
graph
a bunch of nodes with edges between (some of) them directed - only one way between nodes undirected - two ways between nodes
connected graph
a graph with a path between every pair of vertices
acyclic graph
a graph without any cycles
tree
a type of graph that has no cycles
priority queue
an abstract data type that supports insertion of an object and key, removing the object with the minimum key, and decreasing a key: instead of removing oldest/newest item, remove key with highest/lowest priority can be implemented with array, min/max heap, etc.
adjacency matrix
an nxn boolean matrix (where n is the number of nodes) where a true value at matrix[i][j] indicates an edge from i to j can also be an integer matrix of 0s and 1s an undirected graph will be symmetric. a directed graph may not necessarily be
red-black tree
balancing is not as strict, but still guarantees an O(logn) search (as well as insertion and deletion) 1. every node is either red or black 2. root is black 3. leaves, which are null, are considered black 4. every red node must have 2 black children (but black nodes can have black children) i.e. no more than half the nodes in a path can be red 5. every path from a node to its leaves must have the same number of black children
perfect binary tree
binary tree that is both full and complete - all leafs are at the same level, and the level has the max number of nodes these are pretty rare - perfect tree must have exactly (2^k) - 1 nodes, where k is the number of levels
topological sort
can only work if a) there are no cycles in the graph b) the graph is directed ordering the list of nodes in a directed graph such that if (a,b) is an edge in the graph, a will appear before b use a queue to do this. first, create a class variable node.inbound - count inbound edges by walking through each node n and for each of its outgoing edges (n,x), incrementing x.inbound create a queue order, which will eventually hold the valid topological sort. it is empty at the beginning create a queue processNext. this queue will store the next nodes to process assuming all inbound edges are counted, walk through graph again and add all nodes with inbound = 0 to processNext while !processNext.isEmpty() - n = pop() for each node adjacent to n, decrement inbound. if inbound == 0, queue(adjacent). order.queue(n) if order contains all nodes, then it has succeeded. otherwise the graph had a cycle in it
trie (prefix tree)
characters are stored at each node, where each path down the tree represents a word * nodes are used to represent complete words - if node has a * element, then this is a complete word can be used to check valid for valid prefixes of words - this takes O(n) time, where n is the length of the string
find shortest path between two nodes given each node
do a breadth-first search from both nodes - when they collide, you have the shortest path in half length (which is a lot more significant than you would think) given every node has at most k adjacent nodes and the shortest path from node s to node t has length d, this shortens our search time from O(k^d) to O(k^(d/2))
binary tree
each node has at most 2 children
binary search tree
each node has at most 2 children, where all descendants of left are less than descendants of right - must be true for ALL descendant for each node
complete binary tree
every level is fully filled except for last level, which is filled from left to right
full binary tree
every node has either zero or two children - no node has only one child
adjacency list
every vertex or node stores a list of adjacent vertices. you can use a new class for this or you can use an array (or hashtable) of lists to store the adjacency list the most common way to represent a graph
balanced tree
height of both children differ by at most one
min/max-heap
root element is min (max) of tree, where tree is a complete binary tree insert(item) - insert item at next open spot (to preserve completeness) and repeatedly switch with parent i.e. bubble minimum element up - this takes O(logn) time extractMin (or max) - swap root with last item, then remove last element. repeatedly bubble down, switching with smaller (larger) element, until swapped element is back down where it should be
Dijkstra's Algorithm
shortest path between two points in a weighted, directed graph (given positive weights), which may have cycles, by finding minimum weight path from a start node s to EVERY node on the graph pick a starting node a. its distance is 0, all other distances are infinity. put all nodes in a min priority queue for each node n: set it as marked. update all of its adjacent nodes distances - if n's distance plus weight of edge between n and an adjacent node < adjacent node's current distance, update. also for each node, update its previous node pointer to be pointing towards the current node. keep popping until min priority queue is empty
breadth-first search
typically used to find shortest path (or just any path) between two nodes start at the root and explore each neighbor before going on to any neighbor's children. we go wide (hence breadth) and then go deep this is not a recursive algorithm - you must use a queue to implement it iteratively: for any unvisited node, set it as marked and then add it to the queue. first, add root to queue. while queue is not empty, dequeue() and visit the returned node. then for each node in its adjacent list, if it has not been marked, set it as marked and then add it to the queue use a queue, because barbe-queue
depth-first search
typically used to traverse every node in graph, but breadth-first also works start at the root and explore each branch completely (exhaustively) before going on to the next one any kind of tree traversal is a depth-first search, since you always completely explore left branch before right branch when implementing with a graph, you must check if the node has been visited. if not, you can get stuck in an infinite loop use a stack
in-order traversal
visit left branch, node, then right branch
post-order traversal
visit left branch, right branch, then nod (root is always last node visited)
pre-order traversal
visit node, left branch, then right branch (root is always first node visited)