From bda44780778eae3572f1a66a7c38e072d5593aa6 Mon Sep 17 00:00:00 2001 From: sangar-happy Date: Sun, 8 Nov 2020 14:14:05 +0530 Subject: [PATCH 1/4] Move ADT's to adt directory. --- lib/trees/{ => adt}/binary_tree_adt.dart | 2 +- lib/trees/{ => adt}/tree_adt.dart | 0 lib/trees/avl_tree.dart | 2 +- lib/trees/binary_search_tree.dart | 2 +- lib/trees/binary_tree.dart | 2 +- lib/trees/red_black_tree.dart | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename lib/trees/{ => adt}/binary_tree_adt.dart (98%) rename lib/trees/{ => adt}/tree_adt.dart (100%) diff --git a/lib/trees/binary_tree_adt.dart b/lib/trees/adt/binary_tree_adt.dart similarity index 98% rename from lib/trees/binary_tree_adt.dart rename to lib/trees/adt/binary_tree_adt.dart index 8121887..3aef215 100644 --- a/lib/trees/binary_tree_adt.dart +++ b/lib/trees/adt/binary_tree_adt.dart @@ -1,4 +1,4 @@ -import './tree_adt.dart'; +import 'tree_adt.dart'; /// Declares members of a binary node. abstract class BinaryNodeADT, diff --git a/lib/trees/tree_adt.dart b/lib/trees/adt/tree_adt.dart similarity index 100% rename from lib/trees/tree_adt.dart rename to lib/trees/adt/tree_adt.dart diff --git a/lib/trees/avl_tree.dart b/lib/trees/avl_tree.dart index 7283a43..d6c774b 100644 --- a/lib/trees/avl_tree.dart +++ b/lib/trees/avl_tree.dart @@ -1,5 +1,5 @@ // @dart=2.9 -import './binary_tree_adt.dart'; +import 'adt/binary_tree_adt.dart'; import 'binary_search_tree.dart'; import 'binary_tree.dart'; diff --git a/lib/trees/binary_search_tree.dart b/lib/trees/binary_search_tree.dart index 22b08d5..1b58a06 100644 --- a/lib/trees/binary_search_tree.dart +++ b/lib/trees/binary_search_tree.dart @@ -1,5 +1,5 @@ // @dart=2.9 -import './binary_tree_adt.dart'; +import 'adt/binary_tree_adt.dart'; import 'binary_tree.dart'; /// Hierarchical data structure of individual [BinaryNode]s. diff --git a/lib/trees/binary_tree.dart b/lib/trees/binary_tree.dart index 92e6d9e..8384b27 100644 --- a/lib/trees/binary_tree.dart +++ b/lib/trees/binary_tree.dart @@ -1,5 +1,5 @@ // @dart=2.9 -import 'binary_tree_adt.dart'; +import 'adt/binary_tree_adt.dart'; /// A binary node has at most two children, which are referred to as the /// [left] child and the [right] child. diff --git a/lib/trees/red_black_tree.dart b/lib/trees/red_black_tree.dart index 47cb854..8eae3a3 100644 --- a/lib/trees/red_black_tree.dart +++ b/lib/trees/red_black_tree.dart @@ -1,5 +1,5 @@ // @dart=2.9 -import './binary_tree_adt.dart'; +import 'adt/binary_tree_adt.dart'; import 'binary_search_tree.dart'; import 'binary_tree.dart'; From 06f499ea9051ca08661496a806dcdcef9480c1b0 Mon Sep 17 00:00:00 2001 From: Harpreet Sangar Date: Sun, 29 Nov 2020 00:39:42 +0530 Subject: [PATCH 2/4] Add ThreadedBinaryTree --- lib/trees/threaded_binary_tree.dart | 289 ++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 lib/trees/threaded_binary_tree.dart diff --git a/lib/trees/threaded_binary_tree.dart b/lib/trees/threaded_binary_tree.dart new file mode 100644 index 0000000..0a8c5a0 --- /dev/null +++ b/lib/trees/threaded_binary_tree.dart @@ -0,0 +1,289 @@ +import 'adt/binary_tree_adt.dart'; +//import 'binary_tree.dart'; + +/// Data structure similar to [BinaryNode], differs in having two boolean values +/// [leftIsThread] and [rightIsThread] to determine whether [left] and [right] +/// are threads or point to a child instead. +class ThreadedBinaryNode + extends BinaryNodeADT, V> { + V? value; + + /// Stores if [left] is a thread. + bool? leftIsThread; + + /// Stores if [right] is a thread. + bool? rightIsThread; + + /// Creates a node with [value] and marks [left] and [right] to be threads. + ThreadedBinaryNode(this.value, + {this.leftIsThread = true, this.rightIsThread = true}); + + /// Creates a root node with [value]. + ThreadedBinaryNode.root(this.value); +} + +/// A [BinaryTree] is threaded by making all [right] pointers that would +/// normally be null point to the in-order successor of the node +/// (if it exists), and all [left] pointers that would normally be null point +/// to the in-order predecessor of the node. +class ThreadedBinaryTree + extends BinaryTreeADT, V> { + ThreadedBinaryNode? root; + + /// Creates an empty Threaded Binary tree. + ThreadedBinaryTree(); + + /// Creates a Threaded Binary tree with all the values of [list]. + ThreadedBinaryTree.fromList(List list) { + for (var value in list) { + add(value); + } + } + + /// Creates a new Threaded Binary tree with a single [value]. + ThreadedBinaryTree.withSingleValue(V value) + : root = ThreadedBinaryNode.root(value); + + @override + void add(V value) { + if (isEmpty) { + root = ThreadedBinaryNode.root(value); + return; + } + + late ThreadedBinaryNode parent; + var node = root, isPresent = false; + + while (node != null) { + if (node.value!.compareTo(value) == 0) { + isPresent = true; + break; + } + + parent = node; + if (node.value!.compareTo(value) > 0) { + if (!(node.leftIsThread ?? true)) { + node = node.left; + } else { + break; + } + } else if (!(node.rightIsThread ?? true)) { + node = node.right; + } else { + break; + } + } + + if (!isPresent) { + var newNode = ThreadedBinaryNode(value); + + if (parent.value!.compareTo(value) > 0) { + newNode.left = parent.left; + newNode.right = parent; + + // Parent was the leftmost node of the tree. + newNode.leftIsThread = parent.leftIsThread ?? null; + + parent.leftIsThread = false; + parent.left = newNode; + } else { + newNode.left = parent; + newNode.right = parent.right; + + // Parent was the rightmost node of the tree. + newNode.rightIsThread = parent.rightIsThread ?? null; + + parent.rightIsThread = false; + parent.right = newNode; + } + } + } + + @override + bool contains(V value) => inOrder().contains(value); + + @override + void delete(V value) { + ThreadedBinaryNode? parent; + var node = root, isPresent = false; + + while (node != null) { + if (node.value!.compareTo(value) == 0) { + isPresent = true; + break; + } + + parent = node; + if (node.value!.compareTo(value) > 0) { + if (!(node.leftIsThread ?? true)) { + node = node.left; + } else { + break; + } + } else if (!(node.rightIsThread ?? true)) { + node = node.right; + } else { + break; + } + } + + if (isPresent) { + if (!(node!.leftIsThread ?? true) && !(node.rightIsThread ?? true)) { + // Node has 2 children. + _delete(parent, node, _DeleteCase.twoChildren); + } else if (!(node.leftIsThread ?? true)) { + // Node has only a left child. + _delete(parent, node, _DeleteCase.oneChild); + } else if (!(node.rightIsThread ?? true)) { + // Node has only a right child. + _delete(parent, node, _DeleteCase.oneChild); + } else { + // Node is childless. + _delete(parent, node, _DeleteCase.childless); + } + } + } + + @override + List inOrder() { + var result = []; + if (isEmpty) return result; + + var node = root; + while (node!.leftIsThread != null) { + node = node.left; + } + + while (node != null) { + result.add(node.value!); + node = _inOrderSuccessor(node); + } + return result; + } + + @override + List postOrder() { + var result = []; + _postOrder(root, result); + return result; + } + + @override + List preOrder() { + var result = []; + if (isEmpty) return result; + + var node = root; + while (node != null) { + result.add(node.value!); + if (!(node.leftIsThread ?? true)) { + node = node.left; + } else if (!(node.rightIsThread ?? true)) { + node = node.right; + } else { + while (node != null && (node.rightIsThread ?? false)) { + node = node.right; + } + + if (node != null) { + node = node.right; + } + } + } + return result; + } + + void _delete(ThreadedBinaryNode? parent, ThreadedBinaryNode node, + _DeleteCase deleteCase) { + switch (deleteCase) { + case _DeleteCase.childless: + if (parent == null) { + nullify(); + } else if (node == parent.left) { + parent.leftIsThread = node.leftIsThread == null ? null : true; + parent.left = node.left; + } else { + parent.rightIsThread = node.rightIsThread == null ? null : true; + parent.right = node.right; + } + break; + + case _DeleteCase.oneChild: + var child = (node.leftIsThread ?? true) ? node.right : node.left; + + if (parent == null) { + root = child; + } else if (node == parent.left) { + parent.left = child; + } else { + parent.right = child; + } + + var successor = _inOrderSuccessor(node); + var predecessor = _inOrderPredecessor(node); + if (!(node.leftIsThread ?? true)) { + predecessor!.right = successor; + predecessor.rightIsThread = successor == null ? null : true; + } else { + if (!(node.rightIsThread ?? true)) { + successor!.left = predecessor; + successor.leftIsThread = predecessor == null ? null : true; + } + } + break; + + case _DeleteCase.twoChildren: + var successor = node.right!, parentSuccessor = node; + + while (!successor.leftIsThread!) { + parentSuccessor = successor; + successor = successor.left!; + } + + node.value = successor.value; + + if (successor.leftIsThread! && (successor.rightIsThread ?? true)) { + _delete(parentSuccessor, successor, _DeleteCase.childless); + } else { + _delete(parentSuccessor, successor, _DeleteCase.oneChild); + } + break; + } + } + + ThreadedBinaryNode? _inOrderPredecessor(ThreadedBinaryNode node) { + if (node.leftIsThread ?? true) { + return node.left; + } else { + node = node.left!; + + while (!node.rightIsThread!) { + node = node.right!; + } + return node; + } + } + + ThreadedBinaryNode? _inOrderSuccessor(ThreadedBinaryNode node) { + if (node.rightIsThread ?? true) { + return node.right; + } else { + node = node.right!; + + while (!node.leftIsThread!) { + node = node.left!; + } + return node; + } + } + + void _postOrder(ThreadedBinaryNode? node, List list) { + if (node == null) return; + + if (!(node.leftIsThread ?? false)) _postOrder(node.left, list); + if (!(node.rightIsThread ?? false)) _postOrder(node.right, list); + list.add(node.value!); + } +} + +enum _DeleteCase { twoChildren, oneChild, childless } From b5418170318e93375fa39fa2f1fc9605fc3b2b5e Mon Sep 17 00:00:00 2001 From: Harpreet Sangar Date: Sun, 29 Nov 2020 00:40:28 +0530 Subject: [PATCH 3/4] Add test for ThreadedBinaryTree --- test/trees/threaded_binary_tree_test.dart | 197 ++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 test/trees/threaded_binary_tree_test.dart diff --git a/test/trees/threaded_binary_tree_test.dart b/test/trees/threaded_binary_tree_test.dart new file mode 100644 index 0000000..f01332c --- /dev/null +++ b/test/trees/threaded_binary_tree_test.dart @@ -0,0 +1,197 @@ +import 'package:algorithms/trees/threaded_binary_tree.dart'; +import 'package:test/test.dart'; + +void main() { + late ThreadedBinaryTree empty, singleNode, multipleNodes; + + setUp(() { + empty = ThreadedBinaryTree(); + singleNode = ThreadedBinaryTree.withSingleValue('@'); + multipleNodes = ThreadedBinaryTree.fromList( + ['f', 'b', 'g', 'a', 'd', 'i', 'c', 'e', 'h']); + /*--------------------------------------------- + multipleNodes: + f + / \ + b g + / \ \ + a d i + / \ / + c e h + https://en.wikipedia.org/wiki/File:Threaded_tree.svg + ---------------------------------------------*/ + }); + + test('Test empty tree', () { + expect(empty.isEmpty, isTrue); + expect(singleNode.isEmpty, isFalse); + expect(multipleNodes.isEmpty, isFalse); + }); + + test('Test single node', () { + expect(singleNode.root!.value, equals('@')); + expect(multipleNodes.root!.value, equals('f')); + }); + + test('Nullify', () { + var test = ThreadedBinaryTree.fromList([1, 2, 3]); + test.nullify(); + expect(test.isEmpty, isTrue); + }); + + test('Check contains', () { + expect(empty.contains(''), isFalse); + expect(singleNode.contains('@'), isTrue); + expect(singleNode.contains(''), isFalse); + expect(multipleNodes.contains('nope'), isFalse); + + for (var i in ['f', 'b', 'g', 'a', 'd', 'i', 'c', 'e', 'h']) { + expect(multipleNodes.contains(i), isTrue); + } + }); + + group('Traversal', () { + test('Pre-order', () { + expect(empty.preOrder(), []); + expect(singleNode.preOrder(), ['@']); + expect(multipleNodes.preOrder(), + equals(['f', 'b', 'a', 'd', 'c', 'e', 'g', 'i', 'h'])); + }); + + test('Post-order', () { + expect(empty.postOrder(), []); + expect(singleNode.postOrder(), ['@']); + expect(multipleNodes.postOrder(), + equals(['a', 'c', 'e', 'd', 'b', 'h', 'i', 'g', 'f'])); + }); + + test('In-order', () { + expect(empty.inOrder(), []); + expect(singleNode.inOrder(), ['@']); + expect(multipleNodes.inOrder(), + equals(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])); + }); + }); + + test('Add node', () { + var test = ThreadedBinaryTree(); + var list = test.inOrder(); + + test.add(50); + expect( + test.inOrder(), + equals(list + ..add(50) + ..sort())); + + test.add(60); + expect( + test.inOrder(), + equals(list + ..add(60) + ..sort())); + + test.add(70); + expect( + test.inOrder(), + equals(list + ..add(70) + ..sort())); + + test.add(40); + expect( + test.inOrder(), + equals(list + ..add(40) + ..sort())); + + test.add(55); + expect( + test.inOrder(), + equals(list + ..add(55) + ..sort())); + + test.add(75); + expect( + test.inOrder(), + equals(list + ..add(75) + ..sort())); + + test.add(53); + expect( + test.inOrder(), + equals(list + ..add(53) + ..sort())); + + test.add(54); + expect( + test.inOrder(), + equals(list + ..add(54) + ..sort())); + + test.add(30); + expect( + test.inOrder(), + equals(list + ..add(30) + ..sort())); + + test.add(45); + expect( + test.inOrder(), + equals(list + ..add(45) + ..sort())); + + test.add(35); + expect( + test.inOrder(), + equals(list + ..add(35) + ..sort())); + + test.add(51); + expect( + test.inOrder(), + equals(list + ..add(51) + ..sort())); + }); + + test('Delete node', () { + var list = multipleNodes.inOrder(); + + multipleNodes.delete('f'); + expect(multipleNodes.inOrder(), equals(list..remove('f'))); + + multipleNodes.delete('a'); + expect(multipleNodes.inOrder(), equals(list..remove('a'))); + + multipleNodes.delete('i'); + expect(multipleNodes.inOrder(), equals(list..remove('i'))); + + multipleNodes.delete('c'); + expect(multipleNodes.inOrder(), equals(list..remove('c'))); + + multipleNodes.delete('e'); + expect(multipleNodes.inOrder(), equals(list..remove('e'))); + + multipleNodes.delete('d'); + expect(multipleNodes.inOrder(), equals(list..remove('d'))); + + multipleNodes.delete('b'); + expect(multipleNodes.inOrder(), equals(list..remove('b'))); + + multipleNodes.delete('g'); + expect(multipleNodes.inOrder(), equals(list..remove('g'))); + + multipleNodes.delete('h'); + expect(multipleNodes.inOrder(), (list..remove('h'))); + + expect(multipleNodes.isEmpty, isTrue); + }); +} From 03173f7cc22861c1c4d60c3ea9aaa194ad7cdd3a Mon Sep 17 00:00:00 2001 From: Harpreet Sangar Date: Sun, 29 Nov 2020 00:41:36 +0530 Subject: [PATCH 4/4] Add TODO --- test/trees/red_black_tree_test.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/test/trees/red_black_tree_test.dart b/test/trees/red_black_tree_test.dart index 241a191..6031bb4 100644 --- a/test/trees/red_black_tree_test.dart +++ b/test/trees/red_black_tree_test.dart @@ -85,6 +85,7 @@ void main() { }); }); + // TODO: Use isTrue, isFalse and equals(). test('Add node', () { var test = RedBlackTree(); var list = test.inOrder();