Skip to content

Commit 330d5df

Browse files
committed
Added solution for ch4-10 and associated tests.
1 parent 3ffaab1 commit 330d5df

File tree

2 files changed

+136
-0
lines changed

2 files changed

+136
-0
lines changed

src/chapter4/ch4-q10.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
3+
/**
4+
* To check if tree2 is a subtree of tree1 this algorithm basically searches
5+
* through tree1 looking for a potential root node (a node whose value matches
6+
* the root node of tree2). Once found go through node for node comparing from
7+
* that found root down to the nodes in tree2. If they all match then tree2 is
8+
* a subtree.
9+
*
10+
* N = |tree1|
11+
* M = |tree2|
12+
* Time: O(NM)
13+
* Additional space: O(lg N) - space cost is due to recursive nature of algorithm
14+
* and assumes a balanced tree, worst case is O(N)
15+
*/
16+
export function isSubtree(tree1, tree2) {
17+
if (!tree1 || !tree1.root) {
18+
throw new Error('trees1 must be valid non-empty trees');
19+
}
20+
if (!tree2 || !tree2.root) {
21+
return true;
22+
}
23+
return findRoot(tree1.root, tree2.root);
24+
}
25+
26+
function findRoot(node1, node2) {
27+
if (!node1 || !node2) {
28+
return false;
29+
}
30+
else if (node1.val === node2.val && sameTree(node1, node2)) {
31+
return true;
32+
}
33+
else {
34+
return findRoot(node1.left, node2) || findRoot(node1.right, node2);
35+
}
36+
}
37+
38+
function sameTree(node1, node2) {
39+
if (!node1 && !node2) {
40+
return true;
41+
}
42+
else if (!node1 && node2 || node1 && !node2) {
43+
return false;
44+
}
45+
else if (node1.val === node2.val) {
46+
return sameTree(node1.left, node2.left) || sameTree(node1.right, node2.right);
47+
}
48+
else {
49+
return false;
50+
}
51+
}

src/chapter4/ch4-q10.spec.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { expect } from 'chai';
2+
import { Tree } from './helpers';
3+
import * as funcs from './ch4-q10';
4+
5+
for (let key in funcs) {
6+
let func = funcs[key];
7+
8+
describe('ch4-q10: ' + key, function() {
9+
10+
beforeEach(function() {
11+
this.tree1 = new Tree();
12+
this.tree2 = new Tree();
13+
});
14+
15+
it('throws an error if tree1 is null or empty', function() {
16+
expect(() => func(null, null)).to.throw('trees1 must be valid non-empty trees');
17+
expect(() => func(null, this.tree2)).to.throw('trees1 must be valid non-empty trees');
18+
expect(() => func(this.tree1, this.tree2)).to.throw('trees1 must be valid non-empty trees');
19+
this.tree2.add(1);
20+
expect(() => func(this.tree1, this.tree2)).to.throw('trees1 must be valid non-empty trees');
21+
expect(() => func(null, this.tree2)).to.throw('trees1 must be valid non-empty trees');
22+
});
23+
24+
it('returns true where tree2 is null or empty', function() {
25+
this.tree1.add(1);
26+
expect(func(this.tree1, null)).to.be.true;
27+
expect(func(this.tree1, this.tree2)).to.be.true;
28+
});
29+
30+
it('returns right value for simple 3 node balanced tree', function() {
31+
[10, 9, 11].forEach(v => this.tree1.add(v));
32+
this.tree2.root = this.tree1.root.left;
33+
expect(func(this.tree1, this.tree2), 'left node').to.be.true;
34+
this.tree2.root = this.tree1.root.right;
35+
expect(func(this.tree1, this.tree2), 'right node').to.be.true;
36+
this.tree2.root = null;
37+
[10, 9, 11].forEach(v => this.tree2.add(v));
38+
expect(func(this.tree1, this.tree2), 'both same').to.be.true;
39+
});
40+
41+
it('returns false with two different trees', function() {
42+
[8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15].forEach(v => this.tree1.add(v));
43+
[20, 21, 1].forEach(v => this.tree2.add(v));
44+
expect(func(this.tree1, this.tree2)).to.be.false;
45+
this.tree2.root = null;
46+
[8, 2, 6].forEach(v => this.tree2.add(v));
47+
expect(func(this.tree1, this.tree2)).to.be.false;
48+
this.tree2.root = null;
49+
[11, 13, 6].forEach(v => this.tree2.add(v));
50+
expect(func(this.tree1, this.tree2)).to.be.false;
51+
});
52+
53+
it('returns true with balanced tree were it is subtree', function() {
54+
[8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15].forEach(v => this.tree1.add(v));
55+
[2, 1, 3].forEach(v => this.tree2.add(v));
56+
expect(func(this.tree1, this.tree2), '8,4,12').to.be.true;
57+
this.tree2.root = null;
58+
[12, 10, 14, 9, 11, 13, 15].forEach(v => this.tree2.add(v));
59+
expect(func(this.tree1, this.tree2), '8,4,2,1').to.be.true;
60+
this.tree2.root = null;
61+
[3].forEach(v => this.tree2.add(v));
62+
expect(func(this.tree1, this.tree2), '12,14,13,15').to.be.true;
63+
});
64+
65+
it('returns true with balanced tree were it is subtree', function() {
66+
[10, 8, 16, 4, 14, 22, 6, 12, 18, 5, 17, 19].forEach(v => this.tree1.add(v));
67+
[4, 6, 5].forEach(v => this.tree2.add(v));
68+
expect(func(this.tree1, this.tree2), '4, 6, 5').to.be.true;
69+
this.tree2.root = null;
70+
[8, 4, 6, 5].forEach(v => this.tree2.add(v));
71+
expect(func(this.tree1, this.tree2), '8, 4, 6, 5').to.be.true;
72+
this.tree2.root = null;
73+
[16, 14, 22, 12, 18, 17, 19].forEach(v => this.tree2.add(v));
74+
expect(func(this.tree1, this.tree2), '16, 14, 22, 12, 18, 17, 19').to.be.true;
75+
this.tree2.root = null;
76+
[14, 12].forEach(v => this.tree2.add(v));
77+
expect(func(this.tree1, this.tree2), '16, 14, 22, 12, 18, 17, 19').to.be.true;
78+
this.tree2.root = null;
79+
[22, 18, 17, 19].forEach(v => this.tree2.add(v));
80+
expect(func(this.tree1, this.tree2), '16, 14, 22, 12, 18, 17, 19').to.be.true;
81+
});
82+
83+
});
84+
85+
}

0 commit comments

Comments
 (0)