-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Added Kann's algorithm #1676
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added Kann's algorithm #1676
Changes from 3 commits
74e8ec2
9f3aa64
2f2c099
3e9c9e4
f0325a5
99535c5
35fe1c3
ef63f66
154a330
191993f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,45 @@ | ||||||
import Queue from '../Data-Structures/Queue/Queue' | ||||||
|
||||||
/** | ||||||
* @author {RaviSadam} | ||||||
* @name KahnsAlgorithm | ||||||
* @description - | ||||||
* Kahn's Algorithm implementation in JavaScript | ||||||
* @summary | ||||||
* Kahn's Algorithm is used for finding the topological sorting order of directed acyclic graph | ||||||
* | ||||||
* @param graph - Graph (adjacency list) | ||||||
* @param n - Size of graph | ||||||
* @returns {Array} - null if cycle is detected or else result array | ||||||
* | ||||||
*/ | ||||||
|
||||||
export function KahnsAlgorithm(graph, n) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer calling this |
||||||
if (n === null || n === undefined) throw Error('Invalid n was given') | ||||||
const inDegree = Array(n).fill(0) | ||||||
const result = [] | ||||||
for (const neighbours of graph) { | ||||||
for (const neighbour of neighbours) { | ||||||
inDegree[neighbour] += 1 | ||||||
} | ||||||
} | ||||||
const queue = new Queue() | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why use a queue when you can get away with just using JS data structures, specifically, a stack? Also, why call this "queue" rather than giving it a more descriptive name like "roots"? |
||||||
|
||||||
for (let i = 0; i < n; i++) { | ||||||
if (inDegree[i] === 0) { | ||||||
queue.enqueue(i) | ||||||
} | ||||||
} | ||||||
while (queue.length !== 0) { | ||||||
const node = queue.dequeue() | ||||||
result.push(node) | ||||||
for (const neighbour of graph[node]) { | ||||||
inDegree[neighbour] -= 1 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
if (inDegree[neighbour] == 0) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
queue.enqueue(neighbour) | ||||||
} | ||||||
} | ||||||
} | ||||||
if (result.length !== n) return null | ||||||
return result | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/** | ||
* @author {RaviSadam} | ||
* @name kannsAlgorithm | ||
* @description - | ||
* Kann's Algorithm implementation in JavaScript | ||
* @summary | ||
* Kann's Algorithm is used for topological sorting in directed acyclic graphs | ||
* | ||
* @param graph - Graph [[v1,v2],[v3,v4,v5]..] | ||
RaviSadam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* @param n - number of vertices | ||
* @returns {Array} - Empty array if cycle is detected or else result array; | ||
RaviSadam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
*/ | ||
|
||
export function kannsAlgorithm(graph, n) { | ||
if (n === null || n === undefined) throw Error('Invalid n was given') | ||
const inorder = Array(n).fill(0) | ||
RaviSadam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const result = [] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (By the way, you could preallocate this array.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Preallocate could increase the time complexity. At the end we need to check array filled or not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No, not really. It's still linear time in the size of the graph - O(n + m) where n is the number of nodes and m is the number of edges. (Allocation can also optimistically be assumed to be constant time.)
Yes, and that can be done in constant time, if you keep an index to the last element. It'd look something like this: const topoOrder = Array(n) // preallocate
const idx = 0
...
// to push:
topoOrder[idx++] = node
...
if (idx < n) return null; // cyclic Or another variant to write this would be: const topoOrder = Array(n) // preallocate
for (let i = 0; i < n; ++i) {
if (stack.length === 0) return null; // cyclic
const node = stack.pop();
topoOrder[i] = node;
...
} The benefit is that when a topological order exists (which should usually be the case; this is the "happy path"), you can save some reallocations of the array. But this is just a suggestion, I'm fine with pushing to keep the code simple as well. Though perhaps the preallocation variant is a bit more readable, since it makes explicit that we want to have all n nodes be in this array. |
||
for (let entry of graph) { | ||
RaviSadam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for (let edge of entry) { | ||
RaviSadam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
inorder[edge] += 1 | ||
} | ||
} | ||
const queue = [] | ||
RaviSadam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
console.log(inorder) | ||
RaviSadam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for (let i = 0; i < n; i++) { | ||
if (inorder[i] === 0) { | ||
queue.push(i) | ||
} | ||
} | ||
while (queue.length != 0) { | ||
RaviSadam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const node = queue[0] | ||
result.push(node) | ||
queue.splice(0, 1) | ||
RaviSadam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for (let nei of graph[node]) { | ||
RaviSadam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
inorder[nei] -= 1 | ||
if (inorder[nei] == 0) { | ||
RaviSadam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
queue.push(nei) | ||
} | ||
} | ||
} | ||
if (result.length != n) return [] | ||
return result | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { KahnsAlgorithm } from '../KahnsAlgorithm' | ||
|
||
test('Graph without cycle', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As said, these two |
||
const graph = [[], [], [3], [1], [0, 1], [0, 2]] | ||
|
||
expect(KahnsAlgorithm(graph, 6)).toEqual([4, 5, 0, 2, 3, 1]) | ||
}) | ||
|
||
test('Graph with cycle', () => { | ||
const graph = [[2], [], [3, 5], [0, 1], [0, 2]] | ||
|
||
expect(KahnsAlgorithm(graph, 6)).toBe(null) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { kannsAlgorithm } from '../KannsAlgorithm' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You forgot to delete this file. |
||
|
||
appgurueu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
test('Test Case 1: Graph without cycle', () => { | ||
appgurueu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const graph = [[], [], [3], [1], [0, 1], [0, 2]] | ||
|
||
expect(kannsAlgorithm(graph, 6)).toEqual([4, 5, 0, 2, 3, 1]) | ||
}) | ||
|
||
test('Test Case 2: Graph with cycle', () => { | ||
appgurueu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const graph = [[2], [], [3, 5], [0, 1], [0, 2]] | ||
|
||
expect(kannsAlgorithm(graph, 6)).toEqual([]) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant. Just use
graph.length
.