CS62 - Spring 2011 - Lecture 38

  • QUIZ! Problem 16.11

  • Cycle detection
       - How can we determine if an undirected graph has a cycle?
          - run DFS started at some node marking nodes as visited as we go
          - if we try and visit a node that is already marked, we've found a cycle
          - what if the graph isn't connected?
             - we must make sure to run DFS on all of the different parts
          - how can we make sure this happens?
             - traverse all of the nodes, if we find a node that hasn't been visited, run DFS from there

       - look at hasCycles in graph_algorithms.cpp code
          - why do we need to pass the parent?
          - why do we need two methods?
          - what is the parameter passing mechanism used to pass the graph? why?   

       - what is the running time of our cycle detection algorithm?
          - how many times do we call dfsCycle on each vertex?
             - exactly once,
             - the first thing we do is set visited to true for that vertex
             - and will never revisit a visited vertex
          - what is the cost of each call to dfsCycle?
             - depends on the representation
          - adjacency matrix:
             - we need to traverse all V entries to get the neighbors
             - O(|V|^2) overall
          - adjacency list:
             - a little trickier
             - how many times do we process each edge?
                - once
             - O(|V| + |E|), which for a connected graph is O(|E|)

  • connectedness: given an undirected graph, is the graph connected?
       - how might we do this?
          - run depth first search (or breadth first search)
          - keep track of which nodes we visit
          - if we visit all of the nodes, then it's connected

       - first, let's take a look at dfs in graph_algorithms.cpp
          - what are the parameters?
             - the current vertex v
             - the set of visited vertices
             - the graph itself
          - what does it do?
             - just like before, visits the current node
             - then recursively visits the unvisited neighbors
       - what does grop_isConnected do?
          - runs depth first search
             - what is adjMap.begin()->first?
                - adjMap.begin() is an iterator to the first element of the map/graph
                - ->first will give us the key, i.e. the vertex
                - basically, gets any vertex from the graph
          - what does the for loop check?
             - checks to see if there are any unvisited nodes
             - if there are, then it's NOT connected
          - notice again that we need to use a const_iterator
       - what is the running time?
          - Again, we'd like to ask it with respect to |V| and |E|
          - It's going to depend on the graph... what is the worst case running time?
          - what is the running time of grop_isConnected (without the dfs call)?
             - O(|V|)
          - how many times do we call dfs on for each vertex?
             - exactly once
          - what is the cost of each call to dfs?
             - again, will depend on the graph representation
          - adjacency matrix:
             - we need to traverse all V entries to get the neighbors
             - O(|V|^2) overall
          - adjacency list:
             - again, how many times do we process each edge?
                - once
             - O(|V| + |E|), which for a connected graph is O(|E|)
          - is this surprising?
             - NO, it's very similar to cycle detection, utilizing DFS

  • connected components: given an undirected graph, return the largest subgraphs that are connected
       - how does it differ from just asking about connectedness?
          - similar idea as connectedness, but like cycle detection, we need to make sure we examine all of the vertices in the graph
       - how might we do this?   
          - pick a node
          - run depth first search on that node keeping track of both the nodes we visited
          - all nodes visited during THAT dfs are one connected subgraph
          - pick a node that is unvisited and repeat
          - stop when all nodes have been visited
       - we can code this in psudocode:
          void dfs_get_component(vertex v, list of vertices: component) {
             set v as visited
             add v to component
             for (all neighbors u of v){
                if (we have NOT visited u ){
                   dfs_get_component(u, component)
             }       }
          (list of lists of vertices) grop_connected_components(const map<int, list<int> >& adjMap){
             list of lists of vertices connected_components;       
             for (all vertices v){
                if (we have NOT visited v) {                create a new empty list of vertices: current
                   dfs_get_component(v, current)
                   add current to connected_components

             return connected_components

       - dfs_get_component looks is like DFS except we also keep track of the component
          - why doesn't it return anything?
             - the work that is actually done is modifying component
             - what must that mean for component?
                - either it must be a pointer or
                - it must be passed by reference (for our graph assignment, we'll do this option so you don't have to worry about memory management)
       - what does grop_connected_components do?
          - connected_components keeps track of the different subgraphs
          - we traverse each of the vertices, if we haven't visited it, call dfs_get_component
       - if we're representing vertices as ints, what are the types of:
          - component?
             - list<int>
          - connected_componenents?
             - list<list<int> >
       - how are we keeping track of visited?
          - for each call to grop_connected_components, we'll need to keep track of whether or not each node has been visited
          - will need to communicate that to dfs_get_component and make sure it's updated there
       - what is the running time?
          - using similar analysis to the last two approaches O(|V|^2) for adjacency matrix and O(|V| + |E|) for adjacency list

  • random C++ for the day: sizeof
       - the sizeof command allows us to get the number of bytes of a type (built-in types, classes, etc.)
       - it's used by the compiler in a number of places, including for knowing how far to increment a pointer
       - look at sizeof.cpp code
          - we can ask for the size of built-in types like char, int, float and double
          - as well for classes like vector, string and map
          - notice that we can also ask the size of particular variables
       - what do the results tell us?
          - char, int, float and double are all what we would expect
          - for map and vector must use pointers (which we'd expect) since they're the same size, regardless of type
          - arrays are sizeof(arrayType) * number of elements
          - strings must also be implemented with pointers
             - though literal strings are character arrays