mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-04 15:39:42 +08:00
feat: Test running overhaul, switch to Prettier & reformat everything (#1407)
* chore: Switch to Node 20 + Vitest * chore: migrate to vitest mock functions * chore: code style (switch to prettier) * test: re-enable long-running test Seems the switch to Node 20 and Vitest has vastly improved the code's and / or the test's runtime! see #1193 * chore: code style * chore: fix failing tests * Updated Documentation in README.md * Update contribution guidelines to state usage of Prettier * fix: set prettier printWidth back to 80 * chore: apply updated code style automatically * fix: set prettier line endings to lf again * chore: apply updated code style automatically --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Lars Müller <34514239+appgurueu@users.noreply.github.com>
This commit is contained in:
@ -25,7 +25,7 @@ Reference:
|
||||
* @param dest Destination node
|
||||
* @returns Shortest distance from source to destination
|
||||
*/
|
||||
function BellmanFord (graph, V, E, src, dest) {
|
||||
function BellmanFord(graph, V, E, src, dest) {
|
||||
// Initialize distance of all vertices as infinite.
|
||||
const dis = Array(V).fill(Infinity)
|
||||
// initialize distance of source as 0
|
||||
@ -36,7 +36,9 @@ function BellmanFord (graph, V, E, src, dest) {
|
||||
// vertex can have at-most |V| - 1 edges
|
||||
for (let i = 0; i < V - 1; i++) {
|
||||
for (let j = 0; j < E; j++) {
|
||||
if ((dis[graph[j][0]] + graph[j][2]) < dis[graph[j][1]]) { dis[graph[j][1]] = dis[graph[j][0]] + graph[j][2] }
|
||||
if (dis[graph[j][0]] + graph[j][2] < dis[graph[j][1]]) {
|
||||
dis[graph[j][1]] = dis[graph[j][0]] + graph[j][2]
|
||||
}
|
||||
}
|
||||
}
|
||||
// check for negative-weight cycles.
|
||||
@ -44,7 +46,7 @@ function BellmanFord (graph, V, E, src, dest) {
|
||||
const x = graph[i][0]
|
||||
const y = graph[i][1]
|
||||
const weight = graph[i][2]
|
||||
if ((dis[x] !== Infinity) && (dis[x] + weight < dis[y])) {
|
||||
if (dis[x] !== Infinity && dis[x] + weight < dis[y]) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
export class BinaryLifting {
|
||||
constructor (root, tree) {
|
||||
constructor(root, tree) {
|
||||
this.root = root
|
||||
this.connections = new Map()
|
||||
this.up = new Map() // up[node][i] stores the 2^i-th parent of node
|
||||
@ -21,12 +21,12 @@ export class BinaryLifting {
|
||||
this.dfs(root, root)
|
||||
}
|
||||
|
||||
addNode (node) {
|
||||
addNode(node) {
|
||||
// Function to add a node to the tree (connection represented by set)
|
||||
this.connections.set(node, new Set())
|
||||
}
|
||||
|
||||
addEdge (node1, node2) {
|
||||
addEdge(node1, node2) {
|
||||
// Function to add an edge (adds the node too if they are not present in the tree)
|
||||
if (!this.connections.has(node1)) {
|
||||
this.addNode(node1)
|
||||
@ -38,7 +38,7 @@ export class BinaryLifting {
|
||||
this.connections.get(node2).add(node1)
|
||||
}
|
||||
|
||||
dfs (node, parent) {
|
||||
dfs(node, parent) {
|
||||
// The dfs function calculates 2^i-th ancestor of all nodes for i ranging from 0 to this.log
|
||||
// We make use of the fact the two consecutive jumps of length 2^(i-1) make the total jump length 2^i
|
||||
this.up.set(node, new Map())
|
||||
@ -53,7 +53,7 @@ export class BinaryLifting {
|
||||
}
|
||||
}
|
||||
|
||||
kthAncestor (node, k) {
|
||||
kthAncestor(node, k) {
|
||||
// if value of k is more than or equal to the number of total nodes, we return the root of the graph
|
||||
if (k >= this.connections.size) {
|
||||
return this.root
|
||||
@ -69,7 +69,7 @@ export class BinaryLifting {
|
||||
}
|
||||
}
|
||||
|
||||
function binaryLifting (root, tree, queries) {
|
||||
function binaryLifting(root, tree, queries) {
|
||||
const graphObject = new BinaryLifting(root, tree)
|
||||
const ancestors = []
|
||||
for (const [node, k] of queries) {
|
||||
|
@ -1,37 +1,37 @@
|
||||
import Queue from '../Data-Structures/Queue/Queue'
|
||||
|
||||
/**
|
||||
* Breadth-first search is an algorithm for traversing a graph.
|
||||
*
|
||||
* It discovers all nodes reachable from the starting position by exploring all of the neighbor nodes at the present
|
||||
* depth prior to moving on to the nodes at the next depth level.
|
||||
*
|
||||
* (description adapted from https://en.wikipedia.org/wiki/Breadth-first_search)
|
||||
* @see https://www.koderdojo.com/blog/breadth-first-search-and-shortest-path-in-csharp-and-net-core
|
||||
*/
|
||||
export function breadthFirstSearch (graph, startingNode) {
|
||||
// visited keeps track of all nodes visited
|
||||
const visited = new Set()
|
||||
|
||||
// queue contains the nodes to be explored in the future
|
||||
const queue = new Queue()
|
||||
queue.enqueue(startingNode)
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
// start with the queue's first node
|
||||
const node = queue.dequeue()
|
||||
|
||||
if (!visited.has(node)) {
|
||||
// mark the node as visited
|
||||
visited.add(node)
|
||||
const neighbors = graph[node]
|
||||
|
||||
// put all its neighbors into the queue
|
||||
for (let i = 0; i < neighbors.length; i++) {
|
||||
queue.enqueue(neighbors[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return visited
|
||||
}
|
||||
import Queue from '../Data-Structures/Queue/Queue'
|
||||
|
||||
/**
|
||||
* Breadth-first search is an algorithm for traversing a graph.
|
||||
*
|
||||
* It discovers all nodes reachable from the starting position by exploring all of the neighbor nodes at the present
|
||||
* depth prior to moving on to the nodes at the next depth level.
|
||||
*
|
||||
* (description adapted from https://en.wikipedia.org/wiki/Breadth-first_search)
|
||||
* @see https://www.koderdojo.com/blog/breadth-first-search-and-shortest-path-in-csharp-and-net-core
|
||||
*/
|
||||
export function breadthFirstSearch(graph, startingNode) {
|
||||
// visited keeps track of all nodes visited
|
||||
const visited = new Set()
|
||||
|
||||
// queue contains the nodes to be explored in the future
|
||||
const queue = new Queue()
|
||||
queue.enqueue(startingNode)
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
// start with the queue's first node
|
||||
const node = queue.dequeue()
|
||||
|
||||
if (!visited.has(node)) {
|
||||
// mark the node as visited
|
||||
visited.add(node)
|
||||
const neighbors = graph[node]
|
||||
|
||||
// put all its neighbors into the queue
|
||||
for (let i = 0; i < neighbors.length; i++) {
|
||||
queue.enqueue(neighbors[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return visited
|
||||
}
|
||||
|
@ -1,54 +1,54 @@
|
||||
import Queue from '../Data-Structures/Queue/Queue'
|
||||
/**
|
||||
* Breadth-first approach can be applied to determine the shortest path between two nodes in an equi-weighted graph.
|
||||
*
|
||||
* It searches the target node among all neighbors of the starting node, then the process is repeated on the level of
|
||||
* the neighbors of the neighbors and so on.
|
||||
*
|
||||
* @see https://en.wikipedia.org/wiki/Breadth-first_search
|
||||
* @see https://www.koderdojo.com/blog/breadth-first-search-and-shortest-path-in-csharp-and-net-core
|
||||
*/
|
||||
export function breadthFirstShortestPath (graph, startNode, targetNode) {
|
||||
// check if startNode & targetNode are identical
|
||||
if (startNode === targetNode) {
|
||||
return [startNode]
|
||||
}
|
||||
|
||||
// visited keeps track of all nodes visited
|
||||
const visited = new Set()
|
||||
|
||||
// queue contains the paths to be explored in the future
|
||||
const initialPath = [startNode]
|
||||
const queue = new Queue()
|
||||
queue.enqueue(initialPath)
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
// start with the queue's first path
|
||||
const path = queue.dequeue()
|
||||
const node = path[path.length - 1]
|
||||
|
||||
// explore this node if it hasn't been visited yet
|
||||
if (!visited.has(node)) {
|
||||
// mark the node as visited
|
||||
visited.add(node)
|
||||
|
||||
const neighbors = graph[node]
|
||||
|
||||
// create a new path in the queue for each neighbor
|
||||
for (let i = 0; i < neighbors.length; i++) {
|
||||
const newPath = path.concat([neighbors[i]])
|
||||
|
||||
// the first path to contain the target node is the shortest path
|
||||
if (neighbors[i] === targetNode) {
|
||||
return newPath
|
||||
}
|
||||
|
||||
// queue the new path
|
||||
queue.enqueue(newPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the target node was not reachable
|
||||
return []
|
||||
}
|
||||
import Queue from '../Data-Structures/Queue/Queue'
|
||||
/**
|
||||
* Breadth-first approach can be applied to determine the shortest path between two nodes in an equi-weighted graph.
|
||||
*
|
||||
* It searches the target node among all neighbors of the starting node, then the process is repeated on the level of
|
||||
* the neighbors of the neighbors and so on.
|
||||
*
|
||||
* @see https://en.wikipedia.org/wiki/Breadth-first_search
|
||||
* @see https://www.koderdojo.com/blog/breadth-first-search-and-shortest-path-in-csharp-and-net-core
|
||||
*/
|
||||
export function breadthFirstShortestPath(graph, startNode, targetNode) {
|
||||
// check if startNode & targetNode are identical
|
||||
if (startNode === targetNode) {
|
||||
return [startNode]
|
||||
}
|
||||
|
||||
// visited keeps track of all nodes visited
|
||||
const visited = new Set()
|
||||
|
||||
// queue contains the paths to be explored in the future
|
||||
const initialPath = [startNode]
|
||||
const queue = new Queue()
|
||||
queue.enqueue(initialPath)
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
// start with the queue's first path
|
||||
const path = queue.dequeue()
|
||||
const node = path[path.length - 1]
|
||||
|
||||
// explore this node if it hasn't been visited yet
|
||||
if (!visited.has(node)) {
|
||||
// mark the node as visited
|
||||
visited.add(node)
|
||||
|
||||
const neighbors = graph[node]
|
||||
|
||||
// create a new path in the queue for each neighbor
|
||||
for (let i = 0; i < neighbors.length; i++) {
|
||||
const newPath = path.concat([neighbors[i]])
|
||||
|
||||
// the first path to contain the target node is the shortest path
|
||||
if (neighbors[i] === targetNode) {
|
||||
return newPath
|
||||
}
|
||||
|
||||
// queue the new path
|
||||
queue.enqueue(newPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the target node was not reachable
|
||||
return []
|
||||
}
|
||||
|
@ -1,23 +1,27 @@
|
||||
class GraphUnweightedUndirectedAdjacencyList {
|
||||
// Unweighted Undirected Graph class
|
||||
constructor () {
|
||||
constructor() {
|
||||
this.connections = {}
|
||||
}
|
||||
|
||||
addNode (node) {
|
||||
addNode(node) {
|
||||
// Function to add a node to the graph (connection represented by set)
|
||||
this.connections[node] = new Set()
|
||||
}
|
||||
|
||||
addEdge (node1, node2) {
|
||||
addEdge(node1, node2) {
|
||||
// Function to add an edge (adds the node too if they are not present in the graph)
|
||||
if (!(node1 in this.connections)) { this.addNode(node1) }
|
||||
if (!(node2 in this.connections)) { this.addNode(node2) }
|
||||
if (!(node1 in this.connections)) {
|
||||
this.addNode(node1)
|
||||
}
|
||||
if (!(node2 in this.connections)) {
|
||||
this.addNode(node2)
|
||||
}
|
||||
this.connections[node1].add(node2)
|
||||
this.connections[node2].add(node1)
|
||||
}
|
||||
|
||||
DFSComponent (components, node, visited) {
|
||||
DFSComponent(components, node, visited) {
|
||||
// Helper function to populate the visited set with the nodes in each component
|
||||
|
||||
// adding the first visited node in the component to the array
|
||||
@ -28,18 +32,22 @@ class GraphUnweightedUndirectedAdjacencyList {
|
||||
const curr = stack.pop()
|
||||
visited.add(curr.toString())
|
||||
for (const neighbour of this.connections[curr].keys()) {
|
||||
if (!visited.has(neighbour.toString())) { stack.push(neighbour) }
|
||||
if (!visited.has(neighbour.toString())) {
|
||||
stack.push(neighbour)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connectedComponents () {
|
||||
connectedComponents() {
|
||||
// Function to generate the Connected Components
|
||||
// Result is an array containing 1 node from each component
|
||||
const visited = new Set()
|
||||
const components = []
|
||||
for (const node of Object.keys(this.connections)) {
|
||||
if (!visited.has(node.toString())) { this.DFSComponent(components, node, visited) }
|
||||
if (!visited.has(node.toString())) {
|
||||
this.DFSComponent(components, node, visited)
|
||||
}
|
||||
}
|
||||
return components
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ The density of a network is a measure of how many edges exist proportional to
|
||||
how many edges would exist in a complete network (where all possible edges).
|
||||
https://networkx.org/documentation/networkx-1.9/reference/generated/networkx.classes.function.density.html
|
||||
*/
|
||||
function density (numberOfNodes, numberOfEdges, isDirected = false) {
|
||||
function density(numberOfNodes, numberOfEdges, isDirected = false) {
|
||||
const multi = isDirected ? 1 : 2
|
||||
return (multi * numberOfEdges) / (numberOfNodes * (numberOfNodes - 1))
|
||||
}
|
||||
|
@ -1,30 +1,36 @@
|
||||
class GraphUnweightedUndirected {
|
||||
// Unweighted Undirected Graph class
|
||||
constructor () {
|
||||
constructor() {
|
||||
this.connections = {}
|
||||
}
|
||||
|
||||
addNode (node) {
|
||||
addNode(node) {
|
||||
// Function to add a node to the graph (connection represented by set)
|
||||
this.connections[node] = new Set()
|
||||
}
|
||||
|
||||
addEdge (node1, node2) {
|
||||
addEdge(node1, node2) {
|
||||
// Function to add an edge (adds the node too if they are not present in the graph)
|
||||
if (!(node1 in this.connections)) { this.addNode(node1) }
|
||||
if (!(node2 in this.connections)) { this.addNode(node2) }
|
||||
if (!(node1 in this.connections)) {
|
||||
this.addNode(node1)
|
||||
}
|
||||
if (!(node2 in this.connections)) {
|
||||
this.addNode(node2)
|
||||
}
|
||||
this.connections[node1].add(node2)
|
||||
this.connections[node2].add(node1)
|
||||
}
|
||||
|
||||
DFSIterative (node, value) {
|
||||
DFSIterative(node, value) {
|
||||
// DFS Function to search if a node with the given value is present in the graph
|
||||
const stack = [node]
|
||||
const visited = new Set()
|
||||
while (stack.length > 0) {
|
||||
const currNode = stack.pop()
|
||||
// if the current node contains the value being searched for, true is returned
|
||||
if (currNode === value) { return true }
|
||||
if (currNode === value) {
|
||||
return true
|
||||
}
|
||||
// adding the current node to the visited set
|
||||
visited.add(currNode)
|
||||
// adding neighbours in the stack
|
||||
|
@ -1,32 +1,40 @@
|
||||
class GraphUnweightedUndirected {
|
||||
// Unweighted Undirected Graph class
|
||||
constructor () {
|
||||
constructor() {
|
||||
this.connections = {}
|
||||
}
|
||||
|
||||
addNode (node) {
|
||||
addNode(node) {
|
||||
// Function to add a node to the graph (connection represented by set)
|
||||
this.connections[node] = new Set()
|
||||
}
|
||||
|
||||
addEdge (node1, node2) {
|
||||
addEdge(node1, node2) {
|
||||
// Function to add an edge (adds the node too if they are not present in the graph)
|
||||
if (!(node1 in this.connections)) { this.addNode(node1) }
|
||||
if (!(node2 in this.connections)) { this.addNode(node2) }
|
||||
if (!(node1 in this.connections)) {
|
||||
this.addNode(node1)
|
||||
}
|
||||
if (!(node2 in this.connections)) {
|
||||
this.addNode(node2)
|
||||
}
|
||||
this.connections[node1].add(node2)
|
||||
this.connections[node2].add(node1)
|
||||
}
|
||||
|
||||
DFSRecursive (node, value, visited = new Set()) {
|
||||
DFSRecursive(node, value, visited = new Set()) {
|
||||
// DFS Function to search if a node with the given value is present in the graph
|
||||
// checking if the searching node has been found
|
||||
if (node === value) { return true }
|
||||
if (node === value) {
|
||||
return true
|
||||
}
|
||||
// adding the current node to the visited set
|
||||
visited.add(node)
|
||||
// calling the helper function recursively for all unvisited nodes
|
||||
for (const neighbour of this.connections[node]) {
|
||||
if (!visited.has(neighbour)) {
|
||||
if (this.DFSRecursive(neighbour, value, visited)) { return true }
|
||||
if (this.DFSRecursive(neighbour, value, visited)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
@ -6,7 +6,7 @@
|
||||
* It uses graph data structure.
|
||||
*/
|
||||
|
||||
function createGraph (V, E) {
|
||||
function createGraph(V, E) {
|
||||
// V - Number of vertices in graph
|
||||
// E - Number of edges in graph (u,v,w)
|
||||
const adjList = [] // Adjacency list
|
||||
@ -20,7 +20,7 @@ function createGraph (V, E) {
|
||||
return adjList
|
||||
}
|
||||
|
||||
function djikstra (graph, V, src) {
|
||||
function djikstra(graph, V, src) {
|
||||
const vis = Array(V).fill(0)
|
||||
const dist = []
|
||||
for (let i = 0; i < V; i++) dist.push([10000, -1])
|
||||
|
@ -1,5 +1,5 @@
|
||||
// starting at s
|
||||
function solve (graph, s) {
|
||||
function solve(graph, s) {
|
||||
const solutions = {}
|
||||
solutions[s] = []
|
||||
solutions[s].dist = 0
|
||||
@ -10,12 +10,16 @@ function solve (graph, s) {
|
||||
let dist = Infinity
|
||||
|
||||
for (const n in solutions) {
|
||||
if (!solutions[n]) { continue }
|
||||
if (!solutions[n]) {
|
||||
continue
|
||||
}
|
||||
const ndist = solutions[n].dist
|
||||
const adj = graph[n]
|
||||
|
||||
for (const a in adj) {
|
||||
if (solutions[a]) { continue }
|
||||
if (solutions[a]) {
|
||||
continue
|
||||
}
|
||||
|
||||
const d = adj[a] + ndist
|
||||
if (d < dist) {
|
||||
|
@ -9,7 +9,7 @@
|
||||
*/
|
||||
|
||||
class Kosaraju {
|
||||
constructor (graph) {
|
||||
constructor(graph) {
|
||||
this.connections = {}
|
||||
this.reverseConnections = {}
|
||||
this.stronglyConnectedComponents = []
|
||||
@ -20,14 +20,14 @@ class Kosaraju {
|
||||
return this.kosaraju()
|
||||
}
|
||||
|
||||
addNode (node) {
|
||||
addNode(node) {
|
||||
// Function to add a node to the graph (connection represented by set)
|
||||
this.connections[node] = new Set()
|
||||
this.reverseConnections[node] = new Set()
|
||||
this.topoSorted = []
|
||||
}
|
||||
|
||||
addEdge (node1, node2) {
|
||||
addEdge(node1, node2) {
|
||||
// Function to add an edge (adds the node too if they are not present in the graph)
|
||||
if (!(node1 in this.connections) || !(node1 in this.reverseConnections)) {
|
||||
this.addNode(node1)
|
||||
@ -39,7 +39,7 @@ class Kosaraju {
|
||||
this.reverseConnections[node2].add(node1)
|
||||
}
|
||||
|
||||
dfsTopoSort (node, visited) {
|
||||
dfsTopoSort(node, visited) {
|
||||
visited.add(node)
|
||||
for (const child of this.connections[node]) {
|
||||
if (!visited.has(child)) this.dfsTopoSort(child, visited)
|
||||
@ -47,7 +47,7 @@ class Kosaraju {
|
||||
this.topoSorted.push(node)
|
||||
}
|
||||
|
||||
topoSort () {
|
||||
topoSort() {
|
||||
// Function to perform topological sorting
|
||||
const visited = new Set()
|
||||
const nodes = Object.keys(this.connections).map((key) => Number(key))
|
||||
@ -56,7 +56,7 @@ class Kosaraju {
|
||||
}
|
||||
}
|
||||
|
||||
dfsKosaraju (node, visited) {
|
||||
dfsKosaraju(node, visited) {
|
||||
visited.add(node)
|
||||
this.stronglyConnectedComponents[
|
||||
this.stronglyConnectedComponents.length - 1
|
||||
@ -66,7 +66,7 @@ class Kosaraju {
|
||||
}
|
||||
}
|
||||
|
||||
kosaraju () {
|
||||
kosaraju() {
|
||||
// Function to perform Kosaraju Algorithm
|
||||
const visited = new Set()
|
||||
while (this.topoSorted.length > 0) {
|
||||
@ -80,7 +80,7 @@ class Kosaraju {
|
||||
}
|
||||
}
|
||||
|
||||
function kosaraju (graph) {
|
||||
function kosaraju(graph) {
|
||||
const stronglyConnectedComponents = new Kosaraju(graph)
|
||||
return stronglyConnectedComponents
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
class DisjointSetTreeNode {
|
||||
// Disjoint Set Node to store the parent and rank
|
||||
constructor (key) {
|
||||
constructor(key) {
|
||||
this.key = key
|
||||
this.parent = this
|
||||
this.rank = 0
|
||||
@ -9,17 +9,17 @@ class DisjointSetTreeNode {
|
||||
|
||||
class DisjointSetTree {
|
||||
// Disjoint Set DataStructure
|
||||
constructor () {
|
||||
constructor() {
|
||||
// map to from node name to the node object
|
||||
this.map = {}
|
||||
}
|
||||
|
||||
makeSet (x) {
|
||||
makeSet(x) {
|
||||
// Function to create a new set with x as its member
|
||||
this.map[x] = new DisjointSetTreeNode(x)
|
||||
}
|
||||
|
||||
findSet (x) {
|
||||
findSet(x) {
|
||||
// Function to find the set x belongs to (with path-compression)
|
||||
if (this.map[x] !== this.map[x].parent) {
|
||||
this.map[x].parent = this.findSet(this.map[x].parent.key)
|
||||
@ -27,12 +27,12 @@ class DisjointSetTree {
|
||||
return this.map[x].parent
|
||||
}
|
||||
|
||||
union (x, y) {
|
||||
union(x, y) {
|
||||
// Function to merge 2 disjoint sets
|
||||
this.link(this.findSet(x), this.findSet(y))
|
||||
}
|
||||
|
||||
link (x, y) {
|
||||
link(x, y) {
|
||||
// Helper function for union operation
|
||||
if (x.rank > y.rank) {
|
||||
y.parent = x
|
||||
@ -47,26 +47,30 @@ class DisjointSetTree {
|
||||
|
||||
class GraphWeightedUndirectedAdjacencyList {
|
||||
// Weighted Undirected Graph class
|
||||
constructor () {
|
||||
constructor() {
|
||||
this.connections = {}
|
||||
this.nodes = 0
|
||||
}
|
||||
|
||||
addNode (node) {
|
||||
addNode(node) {
|
||||
// Function to add a node to the graph (connection represented by set)
|
||||
this.connections[node] = {}
|
||||
this.nodes += 1
|
||||
}
|
||||
|
||||
addEdge (node1, node2, weight) {
|
||||
addEdge(node1, node2, weight) {
|
||||
// Function to add an edge (adds the node too if they are not present in the graph)
|
||||
if (!(node1 in this.connections)) { this.addNode(node1) }
|
||||
if (!(node2 in this.connections)) { this.addNode(node2) }
|
||||
if (!(node1 in this.connections)) {
|
||||
this.addNode(node1)
|
||||
}
|
||||
if (!(node2 in this.connections)) {
|
||||
this.addNode(node2)
|
||||
}
|
||||
this.connections[node1][node2] = weight
|
||||
this.connections[node2][node1] = weight
|
||||
}
|
||||
|
||||
KruskalMST () {
|
||||
KruskalMST() {
|
||||
// Kruskal's Algorithm to generate a Minimum Spanning Tree (MST) of a graph
|
||||
// Details: https://en.wikipedia.org/wiki/Kruskal%27s_algorithm
|
||||
// getting the edges in ascending order of weights
|
||||
@ -83,7 +87,7 @@ class GraphWeightedUndirectedAdjacencyList {
|
||||
edges.sort((a, b) => a[2] - b[2])
|
||||
// creating the disjoint set
|
||||
const disjointSet = new DisjointSetTree()
|
||||
Object.keys(this.connections).forEach(node => disjointSet.makeSet(node))
|
||||
Object.keys(this.connections).forEach((node) => disjointSet.makeSet(node))
|
||||
// MST generation
|
||||
const graph = new GraphWeightedUndirectedAdjacencyList()
|
||||
let numEdges = 0
|
||||
|
@ -9,14 +9,14 @@
|
||||
import { BinaryLifting } from './BinaryLifting'
|
||||
|
||||
class LCABinaryLifting extends BinaryLifting {
|
||||
constructor (root, tree) {
|
||||
constructor(root, tree) {
|
||||
super(root, tree)
|
||||
this.depth = new Map() // depth[node] stores the depth of node from root
|
||||
this.depth.set(root, 1)
|
||||
this.dfsDepth(root, root)
|
||||
}
|
||||
|
||||
dfsDepth (node, parent) {
|
||||
dfsDepth(node, parent) {
|
||||
// DFS to find depth of every node in the tree
|
||||
for (const child of this.connections.get(node)) {
|
||||
if (child !== parent) {
|
||||
@ -26,10 +26,10 @@ class LCABinaryLifting extends BinaryLifting {
|
||||
}
|
||||
}
|
||||
|
||||
getLCA (node1, node2) {
|
||||
getLCA(node1, node2) {
|
||||
// We make sure that node1 is the deeper node among node1 and node2
|
||||
if (this.depth.get(node1) < this.depth.get(node2)) {
|
||||
[node1, node2] = [node2, node1]
|
||||
;[node1, node2] = [node2, node1]
|
||||
}
|
||||
// We check if node1 is the ancestor of node2, and if so, then return node1
|
||||
const k = this.depth.get(node1) - this.depth.get(node2)
|
||||
@ -48,7 +48,7 @@ class LCABinaryLifting extends BinaryLifting {
|
||||
}
|
||||
}
|
||||
|
||||
function lcaBinaryLifting (root, tree, queries) {
|
||||
function lcaBinaryLifting(root, tree, queries) {
|
||||
const graphObject = new LCABinaryLifting(root, tree)
|
||||
const lowestCommonAncestors = []
|
||||
for (const [node1, node2] of queries) {
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
class Graph {
|
||||
// Generic graph: the algorithm works regardless of direction or weight
|
||||
constructor () {
|
||||
constructor() {
|
||||
this.edges = []
|
||||
}
|
||||
|
||||
addEdge (node1, node2) {
|
||||
addEdge(node1, node2) {
|
||||
// Adding edges to the graph
|
||||
this.edges.push({
|
||||
node1,
|
||||
@ -14,15 +14,15 @@ class Graph {
|
||||
})
|
||||
}
|
||||
|
||||
nodeNeighbors (node) {
|
||||
nodeNeighbors(node) {
|
||||
// Returns an array with all of the node neighbors
|
||||
const neighbors = new Set()
|
||||
for (const edge of this.edges) {
|
||||
// Checks if they have an edge between them and if the neighbor is not
|
||||
// already in the neighbors array
|
||||
if (edge.node1 === node && !(neighbors.has(edge.node2))) {
|
||||
if (edge.node1 === node && !neighbors.has(edge.node2)) {
|
||||
neighbors.add(edge.node2)
|
||||
} else if (edge.node2 === node && !(neighbors.has(edge.node1))) {
|
||||
} else if (edge.node2 === node && !neighbors.has(edge.node1)) {
|
||||
neighbors.add(edge.node1)
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,28 @@
|
||||
import { KeyPriorityQueue } from '../Data-Structures/Heap/KeyPriorityQueue'
|
||||
class GraphWeightedUndirectedAdjacencyList {
|
||||
// Weighted Undirected Graph class
|
||||
constructor () {
|
||||
constructor() {
|
||||
this.connections = {}
|
||||
}
|
||||
|
||||
addNode (node) {
|
||||
addNode(node) {
|
||||
// Function to add a node to the graph (connection represented by set)
|
||||
this.connections[node] = {}
|
||||
}
|
||||
|
||||
addEdge (node1, node2, weight) {
|
||||
addEdge(node1, node2, weight) {
|
||||
// Function to add an edge (adds the node too if they are not present in the graph)
|
||||
if (!(node1 in this.connections)) { this.addNode(node1) }
|
||||
if (!(node2 in this.connections)) { this.addNode(node2) }
|
||||
if (!(node1 in this.connections)) {
|
||||
this.addNode(node1)
|
||||
}
|
||||
if (!(node2 in this.connections)) {
|
||||
this.addNode(node2)
|
||||
}
|
||||
this.connections[node1][node2] = weight
|
||||
this.connections[node2][node1] = weight
|
||||
}
|
||||
|
||||
PrimMST (start) {
|
||||
PrimMST(start) {
|
||||
// Prim's Algorithm to generate a Minimum Spanning Tree (MST) of a graph
|
||||
// Details: https://en.wikipedia.org/wiki/Prim%27s_algorithm
|
||||
const distance = {}
|
||||
@ -26,16 +30,21 @@ class GraphWeightedUndirectedAdjacencyList {
|
||||
const priorityQueue = new KeyPriorityQueue()
|
||||
// Initialization
|
||||
for (const node in this.connections) {
|
||||
distance[node] = (node === start.toString() ? 0 : Infinity)
|
||||
distance[node] = node === start.toString() ? 0 : Infinity
|
||||
parent[node] = null
|
||||
priorityQueue.push(node, distance[node])
|
||||
}
|
||||
// Updating 'distance' object
|
||||
while (!priorityQueue.isEmpty()) {
|
||||
const node = priorityQueue.pop()
|
||||
Object.keys(this.connections[node]).forEach(neighbour => {
|
||||
if (priorityQueue.contains(neighbour) && distance[node] + this.connections[node][neighbour] < distance[neighbour]) {
|
||||
distance[neighbour] = distance[node] + this.connections[node][neighbour]
|
||||
Object.keys(this.connections[node]).forEach((neighbour) => {
|
||||
if (
|
||||
priorityQueue.contains(neighbour) &&
|
||||
distance[node] + this.connections[node][neighbour] <
|
||||
distance[neighbour]
|
||||
) {
|
||||
distance[neighbour] =
|
||||
distance[node] + this.connections[node][neighbour]
|
||||
parent[neighbour] = node
|
||||
priorityQueue.update(neighbour, distance[neighbour])
|
||||
}
|
||||
@ -44,7 +53,7 @@ class GraphWeightedUndirectedAdjacencyList {
|
||||
|
||||
// MST Generation from the 'parent' object
|
||||
const graph = new GraphWeightedUndirectedAdjacencyList()
|
||||
Object.keys(parent).forEach(node => {
|
||||
Object.keys(parent).forEach((node) => {
|
||||
if (node && parent[node]) {
|
||||
graph.addEdge(node, parent[node], this.connections[node][parent[node]])
|
||||
}
|
||||
|
@ -4,10 +4,16 @@ test('Test Case 1', () => {
|
||||
const V = 5
|
||||
const E = 8
|
||||
const destination = 3
|
||||
const graph = [[0, 1, -1], [0, 2, 4],
|
||||
[1, 2, 3], [1, 3, 2],
|
||||
[1, 4, 2], [3, 2, 5],
|
||||
[3, 1, 1], [4, 3, -3]]
|
||||
const graph = [
|
||||
[0, 1, -1],
|
||||
[0, 2, 4],
|
||||
[1, 2, 3],
|
||||
[1, 3, 2],
|
||||
[1, 4, 2],
|
||||
[3, 2, 5],
|
||||
[3, 1, 1],
|
||||
[4, 3, -3]
|
||||
]
|
||||
const dist = BellmanFord(graph, V, E, 0, destination)
|
||||
expect(dist).toBe(-2)
|
||||
})
|
||||
@ -15,10 +21,17 @@ test('Test Case 2', () => {
|
||||
const V = 6
|
||||
const E = 9
|
||||
const destination = 4
|
||||
const graph = [[0, 1, 3], [0, 3, 6],
|
||||
[0, 5, -1], [1, 2, -3],
|
||||
[1, 4, -2], [5, 2, 5],
|
||||
[2, 3, 1], [4, 3, 5], [5, 4, 2]]
|
||||
const graph = [
|
||||
[0, 1, 3],
|
||||
[0, 3, 6],
|
||||
[0, 5, -1],
|
||||
[1, 2, -3],
|
||||
[1, 4, -2],
|
||||
[5, 2, 5],
|
||||
[2, 3, 1],
|
||||
[4, 3, 5],
|
||||
[5, 4, 2]
|
||||
]
|
||||
const dist = BellmanFord(graph, V, E, 0, destination)
|
||||
expect(dist).toBe(1)
|
||||
})
|
||||
@ -26,9 +39,13 @@ test('Test Case 3', () => {
|
||||
const V = 4
|
||||
const E = 5
|
||||
const destination = 1
|
||||
const graph = [[0, 3, -1], [0, 2, 4],
|
||||
[3, 2, 2], [3, 1, 5],
|
||||
[2, 1, -1]]
|
||||
const graph = [
|
||||
[0, 3, -1],
|
||||
[0, 2, 4],
|
||||
[3, 2, 2],
|
||||
[3, 1, 5],
|
||||
[2, 1, -1]
|
||||
]
|
||||
const dist = BellmanFord(graph, V, E, 0, destination)
|
||||
expect(dist).toBe(0)
|
||||
})
|
||||
|
@ -21,8 +21,19 @@ describe('BreadthFirstSearch', () => {
|
||||
*/
|
||||
|
||||
it('should return the visited nodes', () => {
|
||||
expect(Array.from(breadthFirstSearch(graph, 'C'))).toEqual(['C', 'D', 'A', 'B', 'E'])
|
||||
expect(Array.from(breadthFirstSearch(graph, 'A'))).toEqual(['A', 'B', 'D', 'E'])
|
||||
expect(Array.from(breadthFirstSearch(graph, 'C'))).toEqual([
|
||||
'C',
|
||||
'D',
|
||||
'A',
|
||||
'B',
|
||||
'E'
|
||||
])
|
||||
expect(Array.from(breadthFirstSearch(graph, 'A'))).toEqual([
|
||||
'A',
|
||||
'B',
|
||||
'D',
|
||||
'E'
|
||||
])
|
||||
expect(Array.from(breadthFirstSearch(graph, 'F'))).toEqual(['F', 'G'])
|
||||
})
|
||||
})
|
||||
|
@ -21,8 +21,19 @@ describe('BreadthFirstShortestPath', () => {
|
||||
*/
|
||||
|
||||
it('should return the visited nodes', () => {
|
||||
expect(breadthFirstShortestPath(graph, 'C', 'E')).toEqual(['C', 'D', 'A', 'B', 'E'])
|
||||
expect(breadthFirstShortestPath(graph, 'E', 'B')).toEqual(['E', 'D', 'A', 'B'])
|
||||
expect(breadthFirstShortestPath(graph, 'C', 'E')).toEqual([
|
||||
'C',
|
||||
'D',
|
||||
'A',
|
||||
'B',
|
||||
'E'
|
||||
])
|
||||
expect(breadthFirstShortestPath(graph, 'E', 'B')).toEqual([
|
||||
'E',
|
||||
'D',
|
||||
'A',
|
||||
'B'
|
||||
])
|
||||
expect(breadthFirstShortestPath(graph, 'F', 'G')).toEqual(['F', 'G'])
|
||||
expect(breadthFirstShortestPath(graph, 'A', 'G')).toEqual([])
|
||||
})
|
||||
|
Reference in New Issue
Block a user