Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ A collection of JavaScript problems and solutions for studying algorithms.
- [Course Schedule II](src/graph/course-schedule-ii.js)
- [Alien Dictionary](src/graph/alien-dictionary.js)
- [Graph Valid Tree](src/graph/graph-valid-tree.js)
- [Number of Connected Components in an Undirected Graph](src/graph/number-of-connected-components-in-an-undirected-graph.js)

### Linked List

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { assert } from 'chai';
import { serializeUndirectedGraph, deserializeUndirectedGraph } from 'utils/graph-util';
import {
countComponentsDFS,
countComponentsBFS,
countComponentsUnionFind,
} from '../number-of-connected-components-in-an-undirected-graph';

describe('Number of Connected Components in an Undirected Graph', () => {
const testCases = [
[5, [[0, 1], [1, 2], [3, 4]], 2],
[5, [[0, 1], [1, 2], [2, 3], [3, 4]], 1],
[2, [[0, 1], [1, 0]], 1],
];

testCases.forEach((testCase, index) => {
it(`should count the connected components with DFS ${index}`, () => {
const n = testCase[0];
const edges = testCase[1];
const expected = testCase[2];
const actual = countComponentsDFS(n, edges);
assert.equal(actual, expected);
});

it(`should count the connected components with BFS ${index}`, () => {
const n = testCase[0];
const edges = testCase[1];
const expected = testCase[2];
const actual = countComponentsBFS(n, edges);
assert.equal(actual, expected);
});

it(`should count the connected components with union find ${index}`, () => {
const n = testCase[0];
const edges = testCase[1];
const expected = testCase[2];
const actual = countComponentsUnionFind(n, edges);
assert.equal(actual, expected);
});
});
});
168 changes: 168 additions & 0 deletions src/graph/number-of-connected-components-in-an-undirected-graph.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/**
* Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes),
* write a function to find the number of connected components in an undirected graph.
*
* Example 1:
*
* 0 3
* | |
* 1 --- 2 4
* Given n = 5 and edges = [[0, 1], [1, 2], [3, 4]], return 2.
*
* Example 2:
*
* 0 4
* | |
* 1 --- 2 --- 3
* Given n = 5 and edges = [[0, 1], [1, 2], [2, 3], [3, 4]], return 1.
*
* Note:
* You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1]
* is the same as [1, 0] and thus will not appear together in edges.
*/

/**
* DFS Solution
*
* @param {number} n
* @param {number[][]} edges
* @return {number}
*/
export const countComponentsDFS = (n, edges) => {
const adjList = buildGraph(n, edges);

// Traverse all the nodes
const visited = new Set();

let count = 0;

for (let i = 0; i < n; i++) {
if (!visited.has(i)) {
dfs(adjList, i, visited);
count++;
}
}

return count;
};

/**
* BFS Solution
*
* @param {number} n
* @param {number[][]} edges
* @return {number}
*/
export const countComponentsBFS = (n, edges) => {
const adjList = buildGraph(n, edges);

// Traverse all the nodes
const visited = new Set();

let count = 0;

for (let i = 0; i < n; i++) {
if (!visited.has(i)) {
bfs(adjList, i, visited);
count++;
}
}

return count;
};

/**
* Union-Find Solution
*
* @param {number} n
* @param {number[][]} edges
* @return {number}
*/
export const countComponentsUnionFind = (n, edges) => {
const nums = Array(n).fill(-1);

// Step 1. union find
const find = i => {
if (nums[i] === -1) {
return i;
}
return find(nums[i]);
};

for (let i = 0; i < edges.length; i++) {
const x = find(edges[i][0]);
const y = find(edges[i][1]);

// Union
if (x !== y) {
nums[x] = y;
}
}

// Step 2. count the -1
return nums.filter(num => num === -1).length;
};

/**
* DFS tarverse the graph
*
* @param {Map} adjList
* @param {number} u
* @param {Set} visited
*/
const dfs = (adjList, u, visited) => {
visited.add(u);

adjList.get(u).forEach(v => {
if (!visited.has(v)) {
dfs(adjList, v, visited);
}
});
};

/**
* BFS traverse the graph
*
* @param {map} adjList
* @param {number} node
* @param {Set} visited
*/
const bfs = (adjList, node, visited) => {
const queue = [node];
visited.add(node);

while (queue.length > 0) {
const u = queue.shift();

adjList.get(u).forEach(v => {
if (!visited.has(v)) {
queue.push(v);
visited.add(v);
}
});
}
};

/**
* Build the graph using adjacency list
*
* @param {number} n
* @param {number[][]} edges
*/
const buildGraph = (n, edges) => {
const adjList = new Map();

for (let i = 0; i < n; i++) {
adjList.set(i, []);
}

edges.forEach(edge => {
const u = edge[0];
const v = edge[1];

adjList.get(u).push(v);
adjList.get(v).push(u);
});

return adjList;
};