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 breadth-first search in the book Computer Science Guide by William Springer.
The Breadth-First Search algorithm (BFS) transforms an arbitrary graph into a search tree.
When analyzing the runtime of graph algorithms, two notation systems are commonly used:
For example, for graph G, the set of vertices is denoted as V(G) (read as "vertices of G"), and the set of edges is denoted as E(G) (read as "edges of G"). Therefore:
If G is implied by default, the sets can simply be referred to as V and E.
When the algorithm is complete, the depth of each node in the tree is the minimum number of edges (the length of the shortest path) required to reach that node from s, both in the search tree and in the original graph.
To find this path, follow the pointers to the parent nodes until the node s is reached.
Overall, BFS runs in linear time with respect to the size of the input data.
1// Input: An arbitrary graph with one vertex chosen as the root. All vertices2// have the following properties:3//4// distance - initially === Infinity5// parent - initially === null6// marked - initially === false7//8// Output: A spanning tree of the graph in which each vertex is as close to9// the root as possible.1011begin12 let queue = [];13 s.distance = 0;14 s.marked = true;15 queue.push(s);16 while q.length > 0 do17 let activeVertex = queue.shift();18 foreach v in Adj[activeVertex] do19 if v.marked then20 continue21 end22 v.parent = activeVertex;23 v.distance = activeVertex.distance + 124 v.marked = true25 queue.push(v)26 end27 end28end2930// where Adj is the graph represented as an adjacency list.
Both examples are taken from this website.
Example of f a graph implementation.
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) {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 bfs(startVertex, callback) {26 let list = this.vertices; // adjacency list27 let queue = [startVertex]; // queue of vertices to be processed28 let visited = { [startVertex]: 1 }; // visited vertices2930 function handleVertex(vertex) {31 // call the callback function for the visited vertex32 callback(vertex);3334 // get the list of adjacent vertices35 let neighboursList = list[vertex];3637 neighboursList.forEach((neighbour) => {38 if (!visited[neighbour]) {39 visited[neighbour] = 1;40 queue.push(neighbour);41 }42 });43 }4445 // process the vertices in the queue until it is empty46 while (queue.length) {47 let activeVertex = queue.shift();48 handleVertex(activeVertex);49 }5051 queue = Object.keys(this.vertices);5253 // Repeat the process for the remaining unvisited vertices54 while (queue.length) {55 let activeVertex = queue.shift();56 if (!visited[activeVertex]) {57 visited[activeVertex] = 1;58 handleVertex(activeVertex);59 }60 }61 }62}6364const graph = new Graph();6566graph.addVertex("A");67graph.addVertex("B");68graph.addVertex("C");69graph.addVertex("D");70graph.addVertex("E");71graph.addVertex("F");72graph.addVertex("G");73graph.addVertex("H");7475graph.addEdge("A", "B");76graph.addEdge("A", "C");77graph.addEdge("C", "D");78graph.addEdge("C", "E");79graph.addEdge("A", "F");80graph.addEdge("F", "G");8182graph.bfs('A', v => console.log(v));8384// A85// B86// C87// F88// D89// E90// G91// H
Example of a tree implementation.
1// Let's use a simple queue implementation.2class Queue {3 constructor() {4 this.arr = [];5 }6 enqueue(value) {7 this.arr.push(value);8 }9 dequeue() {10 return this.arr.shift();11 }12 isEmpty() {13 return this.arr.length == 0;14 }15}1617class BinaryTreeNode {18 constructor(value) {19 this.left = null;20 this.right = null;21 this.parent = null;22 this.value = value;23 }2425 get height() {26 let leftHeight = this.left ? this.left.height + 1 : 0;27 let rightHeight = this.right ? this.right.height + 1 : 0;28 return Math.max(leftHeight, rightHeight);29 }3031 // When inserting a node, it is important to update all affected references:32// the parent's left or right pointer and the child's parent pointer.33// If the parent already had a child, that child's parent property must be set to null.3435 setLeft(node) {36 if (this.left) {37 this.left.parent = null;38 }39 if (node) {40 this.left = node;41 this.left.parent = this;42 }43 }4445 setRight(node) {46 if (this.right) {47 this.right.parent = null;48 }49 if (node) {50 this.right = node;51 this.right.parent = this;52 }53 }54}5556function traverseBF(root, callback) {57 let nodeQueue = new Queue();58 nodeQueue.enqueue(root);5960 while (!nodeQueue.isEmpty()) {61 let currentNode = nodeQueue.dequeue();6263 // Call the callback function for the current node.64 callback(currentNode);6566 // Add the left child to the queue.67 if (currentNode.left) {68 nodeQueue.enqueue(currentNode.left);69 }7071 // Add the right child to the queue.72 if (currentNode.right) {73 nodeQueue.enqueue(currentNode.right);74 }75 }76}7778let aNode = new BinaryTreeNode('a');7980let bNode = new BinaryTreeNode('b');81aNode.setLeft(bNode);8283let cNode = new BinaryTreeNode('c');84aNode.setRight(cNode);8586let dNode = new BinaryTreeNode('d');87bNode.setRight(dNode);8889let eNode = new BinaryTreeNode('e');90cNode.setLeft(eNode);9192let fNode = new BinaryTreeNode('f');93cNode.setRight(fNode);9495traverseBF(aNode, (node) => console.log(node.value));9697// a98// b99// c100// d101// e102// f
BFS is useful for any problem that requires finding shortest paths.
Route planning with GPS.
If a mapping system stores data about a local area as a graph, where the vertices represent addresses (or intersections) and the edges represent streets (or, more precisely, short road segments), it can use Breadth-First Search to build a search tree with your current location as the source.
A graph is not bipartite if there is an edge connecting the current vertex to an already visited vertex whose distance is either the same or differs by an even number (i.e., the two vertices would be assigned the same color).
Another indication that a graph is not bipartite is the presence of an odd cycle. Such a cycle consists of an edge together with one or more paths to the lowest common ancestor of the two vertices connected by that edge.
A bipartite graph (or bigraph) is a graph whose set of vertices can be divided into two disjoint sets such that every edge connects a vertex from one set to a vertex in the other. In other words, there are no edges between vertices within the same set.
Bipartite graphs are used in: