Loading...
(function(){<br />
let canvas = document.createElement('canvas'),<br />
ctx = canvas.getContext('2d'),<br />
w = canvas.width = innerWidth,<br />
h = canvas.height = innerHeight,<br />
particles = [],<br />
properties = {<br />
bgColor : 'rgba(17, 17, 19, 1)',<br />
particleColor : 'rgba(255, 40, 40, 1)',<br />
particleRadius : 3,<br />
particleCount : 60,<br />
particleMaxVelocity : 0.5,<br />
lineLength : 150,<br />
particleLife : 6,<br />
};<br />
<br />
document.querySelector('body').appendChild(canvas);<br />
<br />
window.onresize = function() {<br />
w = canvas.width = innerWidth;<br />
h = canvas.height = innerHeight;<br />
}<br />
<br />
class Particle {<br />
constructor() {<br />
this.x = Math.random()*w;<br />Loading...
This is a short expanded summary of the section on depth-first search in the book Computer Science Guide by William Springer.
The Depth-First Search (DFS) algorithm transforms an arbitrary graph into a search tree.
When analyzing graph algorithm runtime, two notations are commonly used:
For a graph G, the set of vertices is denoted by V(G) (read as "the vertices of G"), and the set of edges by E(G) (read as "the edges of G"). Therefore, n = |V(G)| (read as "the number of vertices in G" or "the cardinality of the vertex set of G") and m = |E(G)|. If G is understood from the context, the sets can simply be referred to as V and E.
If the search space is too large or infinite, we limit the search depth.
1// Input: an arbitrary graph with one vertex chosen as the root.2// Each vertex has the following properties:3// distance — initially === 04// marked — initially === false56// Output: a spanning tree of the graph.78begin9 let stack = [];10 stack.push(s);11 while stack.length > 0 do12 let activeVertex = stack.pop(s);13 if activeVertex.marked then14 continue15 end16 activeVertex.marked = true;17 foreach v in Adj[activeVertex] do18 if v.marked then19 continue20 end21 v.parent = activeVertex;22 stack.push(v);23 end24 end25end2627// where Adj is the graph represented as an adjacency list,28// and s is the source vertex.
The following implementation is adapted from an example on this website.
1class Graph {2 constructor() {3 this.vertices = {}; // graph adjacency list4 }56 addVertex(value) {7 if (!this.vertices[value]) {8 this.vertices[value] = [];9 }10 }1112 addEdge(vertex1, vertex2) {13 if (!(vertex1 in this.vertices) || !(vertex2 in this.vertices)) {14 throw new Error("There are no such vertices in the graph.");15 }1617 if (!this.vertices[vertex1].includes(vertex2)) {18 this.vertices[vertex1].push(vertex2);19 }20 if (!this.vertices[vertex2].includes(vertex1)) {21 this.vertices[vertex2].push(vertex1);22 }23 }2425 dfs(startVertex, callback) {26 let list = this.vertices; // adjacency list27 let stack = [startVertex]; // stack of vertices to traverse28 // Use an object to store visited vertices,29 // as it makes checking whether a property exists easier.30 let visited = { [startVertex]: 1 }; // visited vertices3132 function handleVertex(vertex) {33 // invoke the callback for the visited vertex34 callback(vertex);3536 // get the list of adjacent vertices37 // When pushing adjacent vertices onto the stack, use reverse()38 // to add them in reverse order. This ensures that the branches39 // are traversed in the same order in which they were originally added.40 let reversedNeighboursList = [...list[vertex]].reverse();4142 reversedNeighboursList.forEach((neighbour) => {43 if (!visited[neighbour]) {44 // mark the vertex as visited45 visited[neighbour] = 1;46 // push onto the stack47 stack.push(neighbour);48 }49 });50 }5152 // process vertices from the stack until it is empty53 while (stack.length) {54 let activeVertex = stack.pop();55 handleVertex(activeVertex);56 }57 // check for disconnected components58 // If the graph may contain isolated vertices or disconnected subgraphs,59 // process any unvisited vertices. If not, this section can be omitted.60 stack = Object.keys(this.vertices);6162 while (stack.length) {63 let activeVertex = stack.pop();64 if (!visited[activeVertex]) {65 visited[activeVertex] = 1;66 handleVertex(activeVertex);67 }68 }69 }70}
Shortest Path Problem: Find a path between two vertices that has the minimum possible weight.
Weighted graphs: Each edge is assigned a weight, which is most commonly a non-negative integer. The weight represents the cost of using that edge.
Variations of the Shortest Path Problem
1// Input: Graph G and source vertex s2// Output: The distance from s to every other vertex in graph G3// Invariant: S is the set of vertices whose shortest-path distances have been determined45// begin6 // Initialize the set of vertices S to the empty set.7 let S = new Set()8 // Initialize the priority queue Q and add all vertices of G to it9 const Q10 while Q.length > 0 do11 let activeVertex = Q.ExtractMin();12 S = S.union(S, {activeVertex})13 forEach v in Adj(activeVertex) do14 Relax(activeVertex, v, w)15 end16 end17end1819// where Adj is the graph represented as an adjacency list.20// s is the source vertex.
In this case, "relaxing" an edge means:
1// Input: Adjacent vertices u and v, and the weight w of the edge between them2// Output: v.d - the weight of the shortest path found from s to v.3// If the path goes through the edge from u to v,4// then the parent of v is assigned the value u56begin7 if(v.d > u.d + w(u, v)) then8 v.d = u.d + w(u,v)9 v.parent = u10 end11end
When a node is removed from the priority queue, we know that we have found the shortest path to this node — any shorter path would have to pass through nodes that have already been processed.
This is a greedy algorithm, but Dijkstra's algorithm is guaranteed to return the optimal solution. Its complexity is O(N²).
"Edge relaxation" takes constant time. For all edges, it takes O(m) in total.
Finding the element with the smallest priority takes O(n) and is performed O(n) times — in total O(N²).
Overall: O(n² + m), where m ≤ n².
The algorithm example is taken from this article..
1function findNearestVertex(distances, visited) {2 let minDistance = Infinity;3 let nearestVertex = null;45 Object.keys(distances).forEach((vertex) => {6 if (!visited[vertex] && distances[vertex] < minDistance) {7 minDistance = distances[vertex];8 nearestVertex = vertex;9 }10 });1112 return nearestVertex;13}1415function dijkstra(graph, startVertex) {16 let visited = {};17 let distances = {}; // shortest paths from the starting vertex18 let previous = {}; // previous vertices19 let vertices = Object.keys(graph); // list of graph vertices2021 // by default, all distances are unknown (infinite)22 vertices.forEach((vertex) => {23 distances[vertex] = Infinity;24 previous[vertex] = null;25 });2627 // the distance to the starting vertex is 028 distances[startVertex] = 0;2930 function handleVertex(vertex) {31 // distance to the vertex32 let activeVertexDistance = distances[vertex];33 // adjacent vertices (with their distances)34 let neighbours = graph[activeVertex];35 // recalculate distances for all adjacent vertices36 neighbours.forEach((item) => {37 const neighbourVertex = item[0];38 const neighbourWeight = item[1];39 // current known distance40 let currentNeighbourDistance = distances[neighbourVertex];41 // calculated distance42 let newNeighbourDistance = activeVertexDistance + neighbourWeight;43 if (newNeighbourDistance < currentNeighbourDistance) {44 distances[neighbourVertex] = newNeighbourDistance;45 previous[neighbourVertex] = vertex;46 }47 });4849 // mark the vertex as visited50 visited[vertex] = 1;51 }5253 // find the closest unprocessed vertex54 let activeVertex = findNearestVertex(distances, visited);5556 // продолжаем цикл, пока остаются необработанные вершины57 while (activeVertex) {58 handleVertex(activeVertex);59 activeVertex = findNearestVertex(distances, visited);60 }6162 return { distances, previous };63}
Let's create the following graph and apply the algorithm:

1class Graph {2 constructor() {3 this.vertices = {}; // adjacency list of the graph4 }56 addVertex(value) {7 if (!this.vertices[value]) {8 this.vertices[value] = [];9 }10 }1112 addEdge(vertex1, vertex2, weight) {13 if (!(vertex1 in this.vertices) || !(vertex2 in this.vertices)) {14 throw new Error("There are no such vertices in the graph");15 }1617 if (!this.vertices[vertex1].includes(vertex2)) {18 this.vertices[vertex1].push([vertex2, weight]);19 }20 if (!this.vertices[vertex2].includes(vertex1)) {21 this.vertices[vertex2].push([vertex1, weight]);22 }23 }24}2526const graph = new Graph();2728graph.addVertex("S");29graph.addVertex("B");30graph.addVertex("C");31graph.addVertex("D");32graph.addVertex("E");33graph.addVertex("F");3435graph.addEdge("S", "B", 14);36graph.addEdge("S", "D", 24);37graph.addEdge("B", "C", 8);38graph.addEdge("B", "F", 4);39graph.addEdge("B", "E", 86);40graph.addEdge("C", "D", 55);41graph.addEdge("C", "F", 1);42graph.addEdge("D", "E", 7);43graph.addEdge("F", "E", 13);4445console.log(dijkstra(graph.vertices, "S"));46// {47// distances: { S: 0, B: 14, C: 19, D: 24, E: 31, F: 18 },48// previous: { S: null, B: 'S', C: 'F', D: 'S', E: 'F', F: 'B' }49// }