Skip to content

Commit

Permalink
Add postorder + fix uses of sort
Browse files Browse the repository at this point in the history
  • Loading branch information
cpettitt committed Sep 24, 2014
1 parent 63d8bc2 commit 0c510b1
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 23 deletions.
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ module.exports = {
dijkstraAll: require("./lib/alg/dijkstra-all"),
findCycles: require("./lib/alg/find-cycles"),
isAcyclic: require("./lib/alg/is-acyclic"),
postorder: require("./lib/alg/postorder"),
preorder: require("./lib/alg/preorder"),
tarjan: require("./lib/alg/tarjan"),
topsort: require("./lib/alg/topsort")
Expand Down
25 changes: 25 additions & 0 deletions lib/alg/postorder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
var _ = require("lodash");

module.exports = postorder;

function postorder(g, root) {
var visited = {},
results = [];

dfs(g, root, undefined, visited, results);

return results;
}

function dfs(g, v, prev, visited, results) {
if (_.has(visited, v)) {
throw new Error("The input graph is not a tree: " + g);
}
visited[v] = true;
g.successors(v).forEach(function(w) {
if (g.isDirected() || w !== prev) {
dfs(g, w, v, visited, results);
}
});
results.push(v);
}
60 changes: 60 additions & 0 deletions test/alg/postorder-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
var _ = require("lodash"),
expect = require("../chai").expect,
Graph = require("../..").Graph,
Digraph = require("../..").Digraph,
postorder = require("../..").alg.postorder;

describe("alg.postorder", function() {
it("returns the root for a singleton graph", function() {
var g = new Graph();
g.setNode("a");
expect(postorder(g, "a")).to.eql(["a"]);
});

it("works for an undirected tree", function() {
var g = new Graph();
g.setEdge("a", "b");
g.setPath(["a", "c", "d"]);
g.setEdge("c", "e");

var result = postorder(g, "a");
expect(_.sortBy(result)).to.eql(["a", "b", "c", "d", "e"]);
expect(result.indexOf("b")).to.be.lt(result.indexOf("a"));
expect(result.indexOf("c")).to.be.lt(result.indexOf("a"));
expect(result.indexOf("d")).to.be.lt(result.indexOf("c"));
expect(result.indexOf("e")).to.be.lt(result.indexOf("c"));
});

it("fails for an undirected graph that is not a tree", function() {
var g = new Graph();
g.setPath(["a", "b", "c", "a"]);

expect(function() { postorder(g, "a"); }).to.throw();
});

it("works for a directed tree", function() {
var g = new Digraph();
g.setEdge("a", "b");
g.setPath(["a", "c", "d"]);
g.setEdge("c", "e");

var result = postorder(g, "a");
expect(_.sortBy(result)).to.eql(["a", "b", "c", "d", "e"]);
expect(result.indexOf("b")).to.be.lt(result.indexOf("a"));
expect(result.indexOf("c")).to.be.lt(result.indexOf("a"));
expect(result.indexOf("d")).to.be.lt(result.indexOf("c"));
expect(result.indexOf("e")).to.be.lt(result.indexOf("c"));
});

it("fails for a directed graph that is not a tree", function() {
var g = new Digraph();
g.setPath(["a", "b", "a"]);
expect(function() { postorder(g, "a"); }).to.throw();
});

it("fails if root is not in the graph", function() {
var g = new Graph();
g.setNode("a");
expect(function() { postorder(g, "b"); }).to.throw();
});
});
7 changes: 4 additions & 3 deletions test/alg/preorder-test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var expect = require("../chai").expect,
var _ = require("lodash"),
expect = require("../chai").expect,
Graph = require("../..").Graph,
Digraph = require("../..").Digraph,
preorder = require("../..").alg.preorder;
Expand All @@ -17,7 +18,7 @@ describe("alg.preorder", function() {
g.setEdge("c", "e");

var result = preorder(g, "a");
expect(result.sort()).to.eql(["a", "b", "c", "d", "e"]);
expect(_.sortBy(result)).to.eql(["a", "b", "c", "d", "e"]);
expect(result.indexOf("a")).to.be.lt(result.indexOf("b"));
expect(result.indexOf("a")).to.be.lt(result.indexOf("c"));
expect(result.indexOf("c")).to.be.lt(result.indexOf("d"));
Expand All @@ -38,7 +39,7 @@ describe("alg.preorder", function() {
g.setEdge("c", "e");

var result = preorder(g, "a");
expect(result.sort()).to.eql(["a", "b", "c", "d", "e"]);
expect(_.sortBy(result)).to.eql(["a", "b", "c", "d", "e"]);
expect(result.indexOf("a")).to.be.lt(result.indexOf("b"));
expect(result.indexOf("a")).to.be.lt(result.indexOf("c"));
expect(result.indexOf("c")).to.be.lt(result.indexOf("d"));
Expand Down
2 changes: 1 addition & 1 deletion test/alg/topsort-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ describe("alg.topsort", function() {
it("returns unsorted nodes for an undirected graph with no edges", function() {
var g = new Graph();
g.setNodes(["a", "b", "c"]);
expect(topsort(g).sort()).to.eql(["a", "b", "c"]);
expect(_.sortBy(topsort(g))).to.eql(["a", "b", "c"]);
});
});
11 changes: 6 additions & 5 deletions test/base-compound-graph-test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var expect = require("./chai").expect;
var _ = require("lodash"),
expect = require("./chai").expect;

exports.tests = tests;

Expand Down Expand Up @@ -30,8 +31,8 @@ function tests(GraphConstructor) {
it("returns all sources for the root (undefined)", function() {
g.setNode("n1");
g.setNode("n2");
expect(g.getChildren().sort()).to.eql(["n1", "n2"]);
expect(g.getChildren(undefined).sort()).to.eql(["n1", "n2"]);
expect(_.sortBy(g.getChildren())).to.eql(["n1", "n2"]);
expect(_.sortBy(g.getChildren(undefined))).to.eql(["n1", "n2"]);
});
});

Expand Down Expand Up @@ -95,7 +96,7 @@ function tests(GraphConstructor) {
g.setNode("n2");

var nestingTree = g.getNestingTree();
expect(nestingTree.sources().sort()).to.eql(["n1", "n2"]);
expect(_.sortBy(nestingTree.sources())).to.eql(["n1", "n2"]);
});

it("returns the hierarchy for nested graphs", function() {
Expand All @@ -105,7 +106,7 @@ function tests(GraphConstructor) {

var nestingTree = g.getNestingTree();
expect(nestingTree.sources()).to.eql(["root"]);
expect(nestingTree.successors("root").sort()).to.eql(["n1-1", "n1-2"]);
expect(_.sortBy(nestingTree.successors("root"))).to.eql(["n1-1", "n1-2"]);
expect(nestingTree.successors("n1-1")).to.eql(["n2"]);
});

Expand Down
2 changes: 1 addition & 1 deletion test/base-graph-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ exports.tests = function(GraphConstructor) {
g.setEdge("n1", "n2");
g.setEdge("n2", "n3");
g.setEdge("n3", "n1");
expect(g.neighbors("n1").sort()).to.eql(["n1", "n2", "n3"]);
expect(_.sortBy(g.neighbors("n1"))).to.eql(["n1", "n2", "n3"]);
});

it("returns undefined if the node is not in the graph", function() {
Expand Down
17 changes: 7 additions & 10 deletions test/digraph-test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var expect = require("./chai").expect,
var _ = require("lodash"),
expect = require("./chai").expect,
baseGraphTest = require("./base-graph-test");

var Digraph = require("..").Digraph;
Expand Down Expand Up @@ -30,7 +31,7 @@ function tests(GraphConstructor) {
g.setEdge("n2", "n3");
g.setEdge("n4", "n3");
g.setNode("n5");
expect(g.sources().sort()).to.eql(["n1", "n4", "n5"]);
expect(_.sortBy(g.sources())).to.eql(["n1", "n4", "n5"]);
});

it("recognizes a new source when in-edges are removed", function() {
Expand All @@ -49,7 +50,7 @@ function tests(GraphConstructor) {
g.setEdge("n3", "n2");
g.setEdge("n3", "n4");
g.setNode("n5");
expect(g.sinks().sort()).to.eql(["n2", "n4", "n5"]);
expect(_.sortBy(g.sinks())).to.eql(["n2", "n4", "n5"]);
});

it("recognizes a new sink when out-edges are removed", function() {
Expand All @@ -68,7 +69,7 @@ function tests(GraphConstructor) {
g.setEdge("n1", "n2");
g.setEdge("n2", "n3");
g.setEdge("n3", "n1");
expect(g.successors("n1").sort()).to.eql(["n1", "n2"]);
expect(_.sortBy(g.successors("n1"))).to.eql(["n1", "n2"]);
});
});

Expand All @@ -78,7 +79,7 @@ function tests(GraphConstructor) {
g.setEdge("n1", "n2");
g.setEdge("n2", "n3");
g.setEdge("n3", "n1");
expect(g.predecessors("n1").sort()).to.eql(["n1", "n3"]);
expect(_.sortBy(g.predecessors("n1"))).to.eql(["n1", "n3"]);
});
});

Expand Down Expand Up @@ -145,9 +146,5 @@ function expectSingleEdgeGraph(g, v, w, label) {
}

function sortEdges(edges) {
return edges.sort(function(e1, e2) {
if (e1.v < e2.v) return -1;
if (e1.v > e2.v) return 1;
return 0;
});
return _.sortBy(edges, function(edge) { return edge.v; });
}
7 changes: 4 additions & 3 deletions test/graph-test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var expect = require("./chai").expect,
var _ = require("lodash"),
expect = require("./chai").expect,
baseGraphTest = require("./base-graph-test");

var Graph = require("..").Graph;
Expand Down Expand Up @@ -50,7 +51,7 @@ function tests(GraphConstructor) {
g.setEdge("n2", "n3");
g.setEdge("n3", "n1");
g.setNode("n4");
expect(g.successors("n1").sort()).to.eql(["n1", "n2", "n3"]);
expect(_.sortBy(g.successors("n1"))).to.eql(["n1", "n2", "n3"]);
});
});

Expand All @@ -61,7 +62,7 @@ function tests(GraphConstructor) {
g.setEdge("n2", "n3");
g.setEdge("n3", "n1");
g.setNode("n4");
expect(g.predecessors("n1").sort()).to.eql(["n1", "n2", "n3"]);
expect(_.sortBy(g.predecessors("n1"))).to.eql(["n1", "n2", "n3"]);
});
});
});
Expand Down

0 comments on commit 0c510b1

Please sign in to comment.