Graphs 
A graph is a collection of nodes called vertices that are connected to one another through edges.
Direction 
Direction relates to the way vertices are connected with one another. If one vertex points to another vertex, but that other vertex does not necessarily point back, then we have a directed graph. If the connection goes both ways, then it is an undirected graph or a bi-directional graph.
Connection 
A graph is considered connected when you can reach any vertex from any other vertex, or that there are no sets of vertices that are disconnected from the other vertices. A graph is considered strongly connected if all of the connections are bi-directional. Otherwise it is weakly connected.
Cycles 
A cycle is when three or more vertices connect to form a closed loop. A graph that has no cycles is called a acyclic graph. A graph with at least one cycle is a cyclic graph. When traversing cyclic graphs you need to be careful to not get into an infinite loop, and instead mark nodes as visited.
Respresenting in code 
Graphs are created using an adjacency list, or list of nodes with their adjacent nodes in another list.
class Vertex:
    def __init__(self, val, edges):
        self.var = val
        self.edges = list(edges) # PointersComplexity 
The space complexity of a graph is O(v+e) where v is the number of vertices and e is the number of edges.
- Depth First Search Traversing: O(v+e)
- Bredth First Search Traversing: O(v+e)
Searching 
To search a graph, you can use either DFS or BFS.
def DFS(graph, start):
    visited = set()  # keep track of the visited nodes
    stack = [start]  # use a stack to keep track of nodes to visit next
    while stack:
        node = stack.pop()  # get the next node to visit
        if node not in visited:
            visited.add(node)  # mark the node as visited
            print(node, end=' ')  # visit the node (print its value in this case)
            stack.extend(graph[node] - visited)  # add the node's neighbors to the stack, able to remove seen nodes in python for efficiency
def BFS(graph, start):
    visited = set()  # Keep track of the nodes that we've visited
    queue = [start]  # Use a queue to implement the BFS
    while queue:
        node = queue.pop()  # Dequeue a node from front of queue
        if node not in visited:
            visited.add(node)  # Mark the node as visited
            print(node, end=' ')  # Visit the node (print its value in this case)
            queue.extend(graph[node])  # Enqueue all neighbours
graph = {
    'A': ['B', 'C'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F'],
    'D': ['B'],
    'E': ['B', 'F'],
    'F': ['C', 'E'],
}
DFS(graph, 'A')  # Output: A C F E B D
BFS(graph, 'A')  # Output: A B C D E FFinding Paths 
The below algorithms find all possible paths from the start to goal. This is assuming all edges have equal weights. If you need to find the shortest path with weighted graphs, you can add the weight to the tuple stored in the stack and find the shortest after all paths have been found.
def dfs_paths(graph, start, goal):
    stack = [(start, [start])]
    while stack:
        (vertex, path) = stack.pop()
        # Again we are using python to subtract nodes already in the path
        # You could also check if next is not in the current path instead
        for next in graph[vertex] - set(path):
            # if next in path: continue
            if next == goal:
                yield path + [next]
            else:
                stack.append((next, path + [next]))
# Will always have shortest path first (if all edges are equal)
def bfs_paths(graph, start, goal):
    queue = [(start, [start])]
    while queue:
        (vertex, path) = queue.pop(0)
        for next in graph[vertex] - set(path):
            # if next in path: continue
            if next == goal:
                yield path + [next]
            else:
                queue.append((next, path + [next]))Finding Shortest Path (Djikstra's Algorithm) 
from collections import defaultdict
class Graph():
    def __init__(self):
        """
        self.edges is a dict of all possible next nodes
        e.g. {'X': ['A', 'B', 'C', 'E'], ...}
        self.weights has all the weights between two nodes,
        with the two nodes as a tuple as the key
        e.g. {('X', 'A'): 7, ('X', 'B'): 2, ...}
        """
        self.edges = defaultdict(list)
        self.weights = {}
    def add_edge(self, from_node, to_node, weight):
        # Note: assumes edges are bi-directional
        self.edges[from_node].append(to_node)
        self.edges[to_node].append(from_node)
        self.weights[(from_node, to_node)] = weight
        self.weights[(to_node, from_node)] = weight
def dijsktra(graph, initial, end):
    # shortest paths is a dict of nodes
    # whose value is a tuple of (previous node, weight)
    shortest_paths = {initial: (None, 0)}
    current_node = initial
    visited = set()
    while current_node != end:
        visited.add(current_node)
        destinations = graph.edges[current_node]
        weight_to_current_node = shortest_paths[current_node][1]
        for next_node in destinations:
            weight = graph.weights[(current_node, next_node)] + weight_to_current_node
            if next_node not in shortest_paths:
                shortest_paths[next_node] = (current_node, weight)
            else:
                current_shortest_weight = shortest_paths[next_node][1]
                if current_shortest_weight > weight:
                    shortest_paths[next_node] = (current_node, weight)
        next_destinations = {node: shortest_paths[node] for node in shortest_paths if node not in visited}
        if not next_destinations:
            return "Route Not Possible"
        # next node is the destination with the lowest weight
        current_node = min(next_destinations, key=lambda k: next_destinations[k][1])
    # Work back through destinations in shortest path
    path = []
    while current_node is not None:
        path.append(current_node)
        next_node = shortest_paths[current_node][0]
        current_node = next_node
    # Reverse path
    path = path[::-1]
    return path