- Published on
Doubly Linked List
3117 words16 min read
- Authors
- Name
- Curtis Warcup
- Visiting/Updating/Checking each vertex in a graph.
- Since we do not have a dedicated starting point, we need to define one.
Graph Traversal Uses
- Peer to peer networking
- Web crawlers
- Finding "closest" matches/recommendations
- Shortest path problems
- GPS Navigation
- Solving mazes
- AI (shortest path to win the game)
Depth First Search Traversal
- explore as far as possible down one brach before "backtracking".
- general idea is we prioritizing children of a given node before we visit siblings.
- in graphs we have NO ROOT NODE.
- follow a graph down, and visit neighbors before you backtrack.
Depth First Search Recursive
DFS(vertex):
if vertex is empty // base case
return (this is base case)
add vertex to results list // build an array and add results
mark vertex as visited // can do this by setting it to true in the results list
for each neighbor in vertex's neighbors:
if neighbor is not visited:
recursively call DFS on neighbor
Here is what we have:
class Graph {
constructor() {
this.adjList = {};
}
addVertex(vertex) {
if (!this.adjList[vertex]) this.adjList[vertex] = [];
}
addEdge(v1, v2) {
this.adjList[v1].push(v2);
this.adjList[v2].push(v1);
}
removeEdge(vertex1, vertex2) {
this.adjList[vertex1] = this.adjList[vertex1].filter((v) => v !== vertex2);
this.adjList[vertex2] = this.adjList[vertex2].filter((v) => v !== vertex1);
}
removeVertex(vertex) {
while (this.adjList[vertex].length) {
const adjacentVertex = this.adjList[vertex].pop();
this.removeEdge(vertex, adjacentVertex);
}
delete this.adjList[vertex];
}
depthFirstRecursive(start) {}
}
let g = new Graph();
g.addVertex('A');
g.addVertex('B');
g.addVertex('C');
g.addVertex('D');
g.addVertex('E');
g.addVertex('F');
g.addEdge('A', 'B');
g.addEdge('A', 'C');
g.addEdge('B', 'D');
g.addEdge('C', 'E');
g.addEdge('D', 'E');
g.addEdge('D', 'F');
g.addEdge('E', 'F');
console.log(g);
Graph { adjList:
{ A: [ 'B', 'C' ],
B: [ 'A', 'D' ],
C: [ 'A', 'E' ],
D: [ 'B', 'E', 'F' ],
E: [ 'C', 'D', 'F' ],
F: [ 'D', 'E' ] } }
// A
// / \
// B C
// | |
// D --- E
// \ /
// F
- The function should accept a starting node
- Create a list to store the end result, to be returned at - the very end
- Create an object to store visited vertices
- Create a helper function which accepts a vertex
- The helper function should return early if the vertex is empty
- The helper function should place the vertex it accepts into the visited object and push that vertex into the - result array.
- Loop over all of the values in the adjacencyList for that vertex
- If any of those values have not been visited, recursively invoke the helper function with that vertex
- Invoke the helper function with the starting vertex
- Return the result array
// start...
depthFirstRecursive(start) {
const results = [];
const visited = {};
const adjList = this.adjList;
function dfs(vertex) {
if (!vertex) return null;
visited[vertex] = true;
results.push(vertex);
console.log(adjList[vertex]); // [ 'B', 'C' ]
}
dfs(start);
}
// final
depthFirstRecursive(start) {
const results = [];
const visited = {};
const adjList = this.adjList;
function dfs(vertex) {
if (!vertex) return null;
visited[vertex] = true;
results.push(vertex);
adjList[vertex].forEach((neighbor) => {
if (!visited[neighbor]) {
return dfs(neighbor);
}
});
}
dfs(start);
return results;
}
console.log(g.depthFirstRecursive('A'));
//
[ 'A', 'B', 'D', 'E', 'C', 'F' ]
DFS Iteratively
DFS-iterative(start):
let S be a stack
S.push(start)
while S is not empty
vertex = S.pop()
if vertex is not labeled as discovered:
visit vertex (add to result list)
label vertex as discovered
for each of vertex's neighbors, N do
S.push(N)
- The function should accept a starting node
- Create a stack to help use keep track of vertices (use a list/array) Create a list to store the end result, to be returned at the very end
- Create an object to store visited vertices
- Add the starting vertex to the stack, and mark it visited
- While the stack has something in it:
- Pop the next vertex from the stack
- If that vertex hasn't been visited yet:
- Mark it as visited
- Add it to the result list
- Push all of its neighbors into the stack
depthFirstIterative(start) {
const stack = [start];
const results = [];
const visited = {};
let currentVertex;
visited[start] = true;
while (stack.length) {
console.log(stack);
currentVertex = stack.pop();
results.push(currentVertex);
// now need to access the neighbors of currentVertex
this.adjList[currentVertex].forEach((neighbor) => {
if (!visited[neighbor]) {
visited[neighbor] = true;
stack.push(neighbor);
}
});
}
return results;
}
let g = new Graph();
g.addVertex('A');
g.addVertex('B');
g.addVertex('C');
g.addVertex('D');
g.addVertex('E');
g.addVertex('F');
g.addEdge('A', 'B');
g.addEdge('A', 'C');
g.addEdge('B', 'D');
g.addEdge('C', 'E');
g.addEdge('D', 'E');
g.addEdge('D', 'F');
g.addEdge('E', 'F');
console.log(g);
// A
// / \
// B C
// | |
// D --- E
// \ /
// F
console.log(g.depthFirstIterative('A'));
//
[ 'A', 'C', 'E', 'F', 'D', 'B' ]
Breadth First Graph Traversal
Visit neighbors at current depth first!
- This function should accept a starting vertex
- Create a queue (you can use an array) and place the starting vertex in it
- Create an array to store the nodes visited
- Create an object to store nodes visited
- Mark the starting vertex as visited
- Loop as long as there is anything in the queue
- Remove the first vertex from the queue and push it into the array that stores nodes visited
- Loop over each vertex in the adjacency list for the vertex you are visiting.
- If it is not inside the object that stores nodes visited, mark it as visited and enqueue that vertex
- Once you have finished looping, return the array of visited nodes
breadthFirst(start) {
const queue = [start];
const results = [];
const visited = {};
let currentVertex;
visited[start] = true;
while (queue.length) {
currentVertex = queue.shift();
results.push(currentVertex);
this.adjList[currentVertex].forEach((neighbor) => {
if (!visited[neighbor]) {
visited[neighbor] = true;
queue.push(neighbor);
}
});
}
return results;
}
let g = new Graph();
g.addVertex('A');
g.addVertex('B');
g.addVertex('C');
g.addVertex('D');
g.addVertex('E');
g.addVertex('F');
g.addEdge('A', 'B');
g.addEdge('A', 'C');
g.addEdge('B', 'D');
g.addEdge('C', 'E');
g.addEdge('D', 'E');
g.addEdge('D', 'F');
g.addEdge('E', 'F');
console.log(g);
// A
// / \
// B C
// | |
// D --- E
// \ /
// F
console.log(g.breadthFirst('A'));
// [ 'A', 'B', 'C', 'D', 'E', 'F' ]
Full code
class Graph {
constructor() {
this.adjList = {}
}
addVertex(vertex) {
if (!this.adjList[vertex]) this.adjList[vertex] = []
}
addEdge(v1, v2) {
this.adjList[v1].push(v2)
this.adjList[v2].push(v1)
}
removeEdge(vertex1, vertex2) {
this.adjList[vertex1] = this.adjList[vertex1].filter((v) => v !== vertex2)
this.adjList[vertex2] = this.adjList[vertex2].filter((v) => v !== vertex1)
}
removeVertex(vertex) {
while (this.adjList[vertex].length) {
const adjacentVertex = this.adjList[vertex].pop()
this.removeEdge(vertex, adjacentVertex)
}
delete this.adjList[vertex]
}
depthFirstRecursive(start) {
const results = []
const visited = {}
const adjList = this.adjList
function dfs(vertex) {
if (!vertex) return null
visited[vertex] = true
results.push(vertex)
adjList[vertex].forEach((neighbor) => {
if (!visited[neighbor]) {
return dfs(neighbor)
}
})
}
dfs(start)
return results
}
depthFirstIterative(start) {
const stack = [start]
const results = []
const visited = {}
let currentVertex
visited[start] = true
while (stack.length) {
console.log(stack)
currentVertex = stack.pop()
results.push(currentVertex)
// now need to access the neighbors of currentVertex
this.adjList[currentVertex].forEach((neighbor) => {
if (!visited[neighbor]) {
visited[neighbor] = true
stack.push(neighbor)
}
})
}
return results
}
breadthFirst(start) {
const queue = [start]
const results = []
const visited = {}
let currentVertex
visited[start] = true
while (queue.length) {
currentVertex = queue.shift()
results.push(currentVertex)
this.adjList[currentVertex].forEach((neighbor) => {
if (!visited[neighbor]) {
visited[neighbor] = true
queue.push(neighbor)
}
})
}
return results
}
}
let g = new Graph()
g.addVertex('A')
g.addVertex('B')
g.addVertex('C')
g.addVertex('D')
g.addVertex('E')
g.addVertex('F')
g.addEdge('A', 'B')
g.addEdge('A', 'C')
g.addEdge('B', 'D')
g.addEdge('C', 'E')
g.addEdge('D', 'E')
g.addEdge('D', 'F')
g.addEdge('E', 'F')