Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 1fa9341

Browse files
fix(tree-rotations): prevent losing nodes
LL Rotation, lose the newParent previous right node; RR Rotation too Fixes: #30
1 parent 3356e1f commit 1fa9341

File tree

3 files changed

+125
-22
lines changed

3 files changed

+125
-22
lines changed

‎src/data-structures/trees/avl-tree.spec.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,23 @@ describe('AvlTree', () => {
144144
expect(tree.root).toBe(n);
145145
});
146146
});
147+
148+
describe('balance without loosing nodes', () => {
149+
beforeEach(() => {
150+
tree.add(16);
151+
tree.add(4);
152+
tree.add(32);
153+
tree.add(8);
154+
tree.add(2);
155+
});
156+
157+
it('should have all nodes', () => {
158+
expect(tree.toArray()).toEqual([16, 4, 32, 2, 8, null, null, null, null, null, null]);
159+
});
160+
161+
it('should rebalance and keep all nodes', () => {
162+
tree.add(1);
163+
expect(tree.toArray()).toEqual([4, 2, 16, 1, null, 8, 32, null, null, null, null, null, null]);
164+
});
165+
});
147166
});

‎src/data-structures/trees/tree-rotations.js

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
/**
33
* Swap parent's child
44
*
5-
*
65
* @example Child on the left side (it also work for the right side)
76
*
87
* p = parent
@@ -34,7 +33,7 @@ function swapParentChild(oldChild, newChild, parent) {
3433
/**
3534
* Single Left Rotation (LL Rotation)
3635
*
37-
* @example: tree with values 1-2-3-4
36+
* @example: #1 tree with values 1-2-3-4
3837
*
3938
* 1 1
4039
* \ \
@@ -43,21 +42,34 @@ function swapParentChild(oldChild, newChild, parent) {
4342
* 3 2* 4
4443
* \
4544
* 4
46-
* @param {TreeNode} node
47-
* @returns {TreeNode} new parent after the rotation
45+
*
46+
* @example: #2 left rotation
47+
*
48+
* 1 1
49+
* \ \
50+
* 4* 16
51+
* / \ / \
52+
* 2 16 -- left-rotation(4) -> 4 32
53+
* / \ / \ \
54+
* 8 32 2 8 64
55+
* \
56+
* 64
57+
* @param {TreeNode} node current node to rotate (e.g. 4)
58+
* @returns {TreeNode} new parent after the rotation (e.g. 16)
4859
*/
4960
function leftRotation(node) {
50-
const newParent = node.right; // E.g., node 3
61+
const newParent = node.right; // E.g., node 16
5162
const grandparent = node.parent; // E.g., node 1
63+
const previousLeft = newParent.left; // E.g., node 8
5264

53-
// swap node 1 left child from 2 to 3.
65+
// swap parent of node 4 from node 1 to node 16
5466
swapParentChild(node, newParent, grandparent);
5567

56-
// Update node 3 left child to be 2, and
57-
// updates node 2 parent to be node 3 (instead of 1).
68+
// Update node 16 left child to be 4, and
69+
// updates node 4 parent to be node 16 (instead of 1).
5870
newParent.setLeftAndUpdateParent(node);
59-
// remove node 2 left child (previouly was node 3)
60-
node.setRightAndUpdateParent(null);
71+
// set node4 right child to be previousLeft (node 8)
72+
node.setRightAndUpdateParent(previousLeft);
6173

6274
return newParent;
6375
}
@@ -66,8 +78,7 @@ function leftRotation(node) {
6678
// tag::rightRotation[]
6779
/**
6880
* Single Right Rotation (RR Rotation)
69-
*
70-
* @example rotate node 3 to the right
81+
* @example: #1 rotate node 3 to the right
7182
*
7283
* 4 4
7384
* / /
@@ -77,22 +88,34 @@ function leftRotation(node) {
7788
* /
7889
* 1
7990
*
91+
* @example: #2 rotate 16 to the right and preserve nodes
92+
* 64 64
93+
* / /
94+
* 16* 4
95+
* / \ / \
96+
* 4 32 -- right-rotation(16) --> 2 16
97+
* / \ / / \
98+
* 2 8 1 8 32
99+
* /
100+
* 1
101+
*
80102
* @param {TreeNode} node
81-
* this is the node we want to rotate to the right. (E.g., node 3)
82-
* @returns {TreeNode} new parent after the rotation (E.g., node 2)
103+
* this is the node we want to rotate to the right. (E.g., node 16)
104+
* @returns {TreeNode} new parent after the rotation (E.g., node 4)
83105
*/
84106
function rightRotation(node) {
85-
const newParent = node.left; // E.g., node 2
86-
const grandparent = node.parent; // E.g., node 4
107+
const newParent = node.left; // E.g., node 4
108+
const grandparent = node.parent; // E.g., node 64
109+
const previousRight = newParent.right; // E.g., node 8
87110

88-
// swap node 4 left children (node 3) with node 2.
111+
// swap node 64's left children (node 16) with node 4 (newParent).
89112
swapParentChild(node, newParent, grandparent);
90113

91-
// update right child on node 2 to be node 3,
92-
// also make node 2 the new parent of node 3.
114+
// update node 4's right child to be node 16,
115+
// also make node 4 the new parent of node 16.
93116
newParent.setRightAndUpdateParent(node);
94-
// remove node 3 left child (so it doesn't point to node 2)
95-
node.setLeftAndUpdateParent(null);
117+
// Update 16's left child to be the `previousRight` node.
118+
node.setLeftAndUpdateParent(previousRight);
96119

97120
return newParent;
98121
}

‎src/data-structures/trees/tree-rotations.spec.js

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,39 @@ describe('Tree rotations', () => {
9696
value: 3, left: 2, right: null, parent: 4,
9797
});
9898
});
99+
100+
it('should not lose nodes on LL', () => {
101+
// 1 1
102+
// \ \
103+
// 4* 16
104+
// / \ / \
105+
// 2 16 -- left-rotation(4) -> 4 32
106+
// / \ / \ \
107+
// 8 32 2 8 64
108+
// \
109+
// 64
110+
const n8 = new BinaryTreeNode(8);
111+
const n16 = new BinaryTreeNode(16);
112+
const n32 = new BinaryTreeNode(32);
113+
const n64 = new BinaryTreeNode(64);
114+
115+
n1.setRightAndUpdateParent(n4);
116+
n4.setLeftAndUpdateParent(n2);
117+
n4.setRightAndUpdateParent(n16);
118+
n16.setLeftAndUpdateParent(n8);
119+
n16.setRightAndUpdateParent(n32);
120+
n32.setLeftAndUpdateParent(n64);
121+
122+
const newParent = leftRotation(n4);
123+
124+
expect(newParent).toBe(n16);
125+
expect(n8.toValues()).toMatchObject({
126+
value: 8, left: null, right: null, parent: 4,
127+
});
128+
expect(n4.toValues()).toMatchObject({
129+
value: 4, left: 2, right: 8, parent: 16,
130+
});
131+
});
99132
});
100133

101134
describe('#rightRotation (RR Rotation)', () => {
@@ -145,7 +178,7 @@ describe('Tree rotations', () => {
145178
});
146179
});
147180

148-
it('should last two', () => {
181+
it('should RR last two', () => {
149182
// 1
150183
// \
151184
// 3*
@@ -167,6 +200,34 @@ describe('Tree rotations', () => {
167200
value: 3, left: null, right: null, parent: 2,
168201
});
169202
});
203+
204+
it('should not lose nodes on RR', () => {
205+
// 16* 4
206+
// / \ / \
207+
// 4 32 -- right-rotation(16) --> 2 16
208+
// / \ / / \
209+
// 2 8 1 8 32
210+
// /
211+
// 1
212+
const n8 = new BinaryTreeNode(8);
213+
const n16 = new BinaryTreeNode(16);
214+
const n32 = new BinaryTreeNode(32);
215+
n16.setLeftAndUpdateParent(n4);
216+
n16.setRightAndUpdateParent(n32);
217+
n4.setLeftAndUpdateParent(n2);
218+
n4.setRightAndUpdateParent(n8);
219+
n2.setLeftAndUpdateParent(n1);
220+
221+
const newParent = rightRotation(n16);
222+
223+
expect(newParent).toBe(n4);
224+
expect(n8.toValues()).toMatchObject({
225+
value: 8, left: null, right: null, parent: 16,
226+
});
227+
expect(n16.toValues()).toMatchObject({
228+
value: 16, left: 8, right: 32, parent: 4,
229+
});
230+
});
170231
});
171232

172233
describe('#leftRightRotation (LR Rotation)', () => {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /