Advertisement
gelita

graph traversal

Feb 25th, 2020
278
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.  *  Target Practice 08 - Graph Traversal
  3.  */
  4.  
  5. 'use strict';
  6.  
  7. /**
  8.  *  1. For the example graph below, what an expected output if we printed
  9.  *     each vertex value from vertex 0 outwards using:
  10.  *
  11.  *     a. BREADTH FIRST traversal: [0,1,2,7,4,6,5,3]
  12.  *     b. DEPTH FIRST traversal: [0,1,2,4,5,3,6,7]
  13.  * }}}
  14.  *
  15.  *     NOTE: The traversal should take care of redundancy and not print the same
  16.  *           vertex value twice.
  17.  *
  18.  *     Example Graph:
  19.  *
  20.  *              2
  21.  *            /   \
  22.  *    0 ~~~ 1       4  ~~~ 5 ~~~ 3
  23.  *            \   /   \   /
  24.  *              7       6
  25.  */
  26.  
  27.  
  28. // DO NOT EDIT
  29. // Vertex class
  30. class Vertex {
  31.   constructor(id) {
  32.     this.id = id !== undefined ? id : null;
  33.     this.edges = [];
  34.   }
  35. }
  36.  
  37. // DO NOT EDIT
  38. // generate graph from int and array of arrays
  39. function deserialize(n, edges) {
  40.   let vertices = {};
  41.   while (n--) {
  42.     vertices[n] = new Vertex(n);
  43.   }
  44.   let v1;
  45.   let v2;
  46.   edges.forEach(edge => {
  47.     v1 = edge[0];
  48.     v2 = edge[1];
  49.     vertices[v1].edges.push(vertices[v2]);
  50.     vertices[v2].edges.push(vertices[v1]);
  51.   });
  52.  
  53.   return vertices[0];
  54. }
  55.  
  56. // DO NOT EDIT
  57. // sampleGraph is the vertex with id 0
  58. const sampleGraph = deserialize(8, [[0, 1], [1, 2], [2, 4], [3, 5], [4, 5],
  59. [1, 7], [4, 6], [4, 7], [5, 6]]);
  60.  
  61. /**
  62.  *  1. Implement Breadth First Search using a queue and while loop.
  63.  *
  64.  *  Input: {Vertex} - the starting vertex
  65.  *  Output: {Array} - an array of vertex ids of the path
  66.  *
  67.  *  NOTE: You may use an array or linked list for your queue.
  68.  *
  69.  *  HINT: Use a set or hash table to handle redundancy
  70.  */
  71.  
  72. function bfs(vertex) {
  73.   let queue = [];
  74.   let result = [];
  75.   let visited = new Set();
  76.  
  77.   queue.push(vertex);
  78.   visited.add(vertex);
  79.  
  80.   while (queue.length > 0) {
  81.     let current = queue.shift();
  82.    
  83.     for (let edge of current.edges) {
  84.       if (!visited.has(edge)) {
  85.         queue.push(edge);
  86.         visited.add(edge);
  87.       }
  88.     }
  89.     result.push(current.id);
  90.   }
  91.   console.log(result);
  92.   return result;
  93. }
  94.  
  95. /**
  96.  *  2. Given a starting vertex, and an integer destination, return all valid
  97.  *     paths for a given source and destination.
  98.  *
  99.  *  Input: {Vertex} - the starting vertex
  100.  *         {Destination} - integer value of the destination vertex
  101.  *  Output: {Array} - an array of arrays of integers
  102.  *
  103.  *  HINT: Use a set or hash map to handle redundancy
  104.  *
  105.  *  NOTE: Please be aware that this problem is a slight variation
  106.  *    of the HackerRank challenge that was provided in class. How would you
  107.  *    handle things differently if each path returned needed to be a list?
  108.  */
  109.  
  110. function dfs(vertex, destination) {
  111.   let result = [];
  112.   let visited = new Set();
  113.    
  114.   function traverse(current, path) {
  115.     if (current.id === destination) {
  116.       result.push(path.slice());
  117.       return;
  118.     }
  119.     if (visited.has(current)) {
  120.       return;
  121.     }
  122.    
  123.     visited.add(current);
  124.     for (let edge of current.edges) {
  125.       traverse(edge, path.concat(edge.id));
  126.     }
  127.     visited.delete(current);
  128.    
  129.    
  130.   }
  131.  
  132.   traverse(vertex, [vertex.id]);
  133.  
  134.   console.log(result);
  135.   return result;
  136. }
  137.  
  138.  
  139. ////////////////////////////////////////////////////////////
  140. ///////////////  DO NOT TOUCH TEST BELOW!!!  ///////////////
  141. ////////////////////////////////////////////////////////////
  142.  
  143.  
  144. function assert(count, name, test) {
  145.   if (!count || !Array.isArray(count) || count.length !== 2) {
  146.     count = [0, '*'];
  147.   } else {
  148.     count[1]++;
  149.   }
  150.  
  151.   let pass = 'false';
  152.   let errMsg = null;
  153.   try {
  154.     if (test()) {
  155.       pass = ' true';
  156.       count[0]++;
  157.     }
  158.   } catch (e) {
  159.     errMsg = e;
  160.   }
  161.   console.log('  ' + (count[1] + ')   ').slice(0, 5) + pass + ' : ' + name);
  162.   if (errMsg !== null) {
  163.     console.log('       ' + errMsg + '\n');
  164.   }
  165. }
  166.  
  167. function arraysMatching(arr1, arr2) {
  168.   if (arr1.length !== arr2.length) { return false; }
  169.  
  170.   let cache = {};
  171.   for (let i = 0; i < arr1.length; i++) {
  172.     if (cache[arr1[i]] === undefined) {
  173.       cache[arr1[i]] = 1;
  174.     } else {
  175.       cache[arr1[i]]++;
  176.     }
  177.   }
  178.  
  179.   for (let j = 0; j < arr2.length; j++) {
  180.     if (cache[arr2[j]] === undefined || cache[arr2[j]] === 0) { return false; }
  181.     cache[arr2[j]]--;
  182.   }
  183.   return true;
  184. }
  185.  
  186. function getNeighbors(vertex, visited) {
  187.   let edges = vertex.edges;
  188.   return edges.filter((neighbor) => {
  189.     return visited[neighbor.id] === undefined;
  190.   });
  191. }
  192.  
  193. function getValues(vertices) {
  194.   return vertices.map((vertex) => {
  195.     return vertex.id;
  196.   });
  197. }
  198.  
  199. function removeVisited(vertices, visited) {
  200.   return vertices.filter((vertex) => {
  201.     return !visited.has(vertex);
  202.   });
  203. }
  204.  
  205. // takes in an array of path and a vertex start point and determines whether
  206. // the bfs is valid
  207. function validBfs(path, start) {
  208.   if (path[0] !== start.id) { return false; }
  209.  
  210.   let current = start;
  211.   let visited = {};
  212.   visited[current.id] = current;
  213.   for (let i = 0, j = 1; i < path.length; i++) {
  214.     if (i >= j) { return false; }
  215.     let neighbors = getNeighbors(current, visited);
  216.     let values = getValues(neighbors);
  217.     if (!arraysMatching(values, path.slice(j, j + values.length))) {
  218.       return false;
  219.     }
  220.     j += values.length;
  221.     neighbors.forEach((vertex) => {
  222.       visited[vertex.id] = vertex;
  223.     });
  224.     current = visited[path[i + 1]];
  225.   }
  226.  
  227.   return true;
  228. }
  229.  
  230. function validDfs(paths, source, destination) {
  231.  
  232.   for (let path of paths) {
  233.     if (path[0] !== source.id) {
  234.       return false;
  235.     }
  236.     if (path[path.length - 1] !== destination) {
  237.       return false;
  238.     }
  239.     let current = source;
  240.     let isValid = false;
  241.     for (let node = 1; node < path.length; node++) {
  242.       let neighbors = current.edges;
  243.       for (let neighbor of neighbors) {
  244.         if (neighbor.id === path[node]) {
  245.           isValid = true;
  246.           current = neighbor;
  247.           break;
  248.         }
  249.       }
  250.       if (isValid === false) {
  251.         return false;
  252.       }
  253.     }
  254.   }
  255.  
  256.   /* sometimes you hit here if the input is an empty array */
  257.   /* check that there is a valid path through doing dfs */
  258.  
  259.   function testDfs(current) {
  260.     if (visited.has(current.id)) { return false; }
  261.     visited.add(current.id);
  262.     if (current.id === destination) { return true; }
  263.     let neighbors = current.edges;
  264.     for (let neighbor of neighbors) {
  265.       if (visited.has(neighbor.id) == false) {
  266.         if (testDfs(neighbor)) { return true; }
  267.       }
  268.     }
  269.     visited.delete(current.id);
  270.     return false;
  271.   }
  272.  
  273.   let visited = new Set();
  274.   if (testDfs(source) && (paths === undefined || paths.length == 0)) { return false; }
  275.   return true;
  276.  
  277. }
  278.  
  279.  
  280. const testGraph = deserialize(8, [[0, 1], [1, 2], [2, 4], [3, 5], [4, 5],
  281. [1, 7], [4, 6], [4, 7], [5, 6]]);
  282.  
  283. console.log('Breadth First Search tests');
  284. let testCount = [0, 0];
  285.  
  286. assert(testCount, 'able to return the elements of a graph in breadth first manner', () => {
  287.   let results = bfs(testGraph);
  288.   return validBfs(results, testGraph);
  289. });
  290.  
  291. assert(testCount, 'should return only starting initial node if no edges exist in graph', () => {
  292.   let noEdgeGraph = deserialize(8, []);
  293.   let results = bfs(noEdgeGraph);
  294.   return validBfs(results, noEdgeGraph);
  295. });
  296.  
  297. assert(testCount, 'should return elements of simple graph: 0 - 1 - 2 starting at 0', () => {
  298.   let simpleGraph = deserialize(3, [[0, 1], [1, 2]]);
  299.   let results = bfs(simpleGraph);
  300.   return validBfs(results, simpleGraph);
  301. });
  302.  
  303. console.log('PASSED: ' + testCount[0] + ' / ' + testCount[1], '\n\n');
  304.  
  305.  
  306. console.log('Depth First Search tests');
  307. testCount = [0, 0];
  308.  
  309. assert(testCount, 'should return valid dfs for connected graph with a cycle', () => {
  310.   let results = dfs(testGraph, 3);
  311.   let destination = 3;
  312.   return validDfs(results, testGraph, destination);
  313. });
  314.  
  315. assert(testCount, 'should return valid dfs for connected graph with no cycle', () => {
  316.   let source = deserialize(6, [[0, 1], [1, 5], [1, 2], [2, 4], [4, 3]]);
  317.   let destination = 3;
  318.   let results = dfs(source, destination);
  319.   return validDfs(results, source, destination);
  320. });
  321.  
  322. assert(testCount, 'should return valid dfs for unconnected graphs with a path', () => {
  323.   let source = deserialize(7, [[5, 1], [5, 6], [1, 2], [2, 4], [0, 3]]);
  324.   let destination = 3;
  325.   let results = dfs(source, destination);
  326.   return validDfs(results, source, destination);
  327. });
  328.  
  329. assert(testCount, 'should return valid dfs for unconnected graphs with no path', () => {
  330.   let source = deserialize(7, [[0, 1], [1, 6], [1, 2], [2, 4], [5, 3]]);
  331.   let destination = 3;
  332.   let results = dfs(source, destination);
  333.   return validDfs(results, source, destination);
  334. });
  335.  
  336. console.log('PASSED: ' + testCount[0] + ' / ' + testCount[1], '\n\n');
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement