diff --git a/01-tree_successor/cpp/Makefile b/01-tree_successor/cpp/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..9feafbe75fdce9e90992dd6e409d359fea098b3c --- /dev/null +++ b/01-tree_successor/cpp/Makefile @@ -0,0 +1,12 @@ +test: tree_successor_test + ./$< + +CXXFLAGS=-std=c++11 -O2 -Wall -Wextra -g -Wno-sign-compare + +tree_successor_test: tree_successor.h tree_successor_test.cpp test_main.cpp + $(CXX) $(CXXFLAGS) $^ -o $@ + +clean: + rm -f tree_successor_test + +.PHONY: clean test diff --git a/01-tree_successor/cpp/test_main.cpp b/01-tree_successor/cpp/test_main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f4aff0785f636b7fd0ea1a15aa69dafe06f290f --- /dev/null +++ b/01-tree_successor/cpp/test_main.cpp @@ -0,0 +1,43 @@ +#include <cstdlib> +#include <functional> +#include <iostream> +#include <string> +#include <utility> +#include <vector> + +using namespace std; + +extern vector<pair<string, function<void()>>> tests; + +void expect_failed(const string& message) { + cerr << "Test error: " << message << endl; + exit(1); +} + +int main(int argc, char* argv[]) { + vector<string> required_tests; + + if (argc > 1) { + required_tests.assign(argv + 1, argv + argc); + } else { + for (const auto& test : tests) + required_tests.push_back(test.first); + } + + for (const auto& required_test : required_tests) { + bool found = false; + for (const auto& test : tests) + if (required_test == test.first) { + cerr << "Running test " << required_test << endl; + test.second(); + found = true; + break; + } + if (!found) { + cerr << "Unknown test " << required_test << endl; + return 1; + } + } + + return 0; +} diff --git a/01-tree_successor/cpp/tree_successor.h b/01-tree_successor/cpp/tree_successor.h new file mode 100644 index 0000000000000000000000000000000000000000..d1306f6ea2e0fa3af6674da904e72898fe1dcc45 --- /dev/null +++ b/01-tree_successor/cpp/tree_successor.h @@ -0,0 +1,73 @@ +// A node of the tree +class Node { + public: + int key; + Node* left; + Node* right; + Node* parent; + + // Constructor + Node(int key, Node* parent=nullptr) { + this->key = key; + this->parent = parent; + this->left = nullptr; + this->right = nullptr; + } +}; + +// Binary tree +class Tree { + public: + // Pointer to root of the tree; nullptr if the tree is empty. + Node* root = nullptr; + + // Insert a key into the tree. + // If the key is already present, nothing happens. + void insert(int key) { + if (!root) { + root = new Node(key); + return; + } + + Node* node = root; + while (node->key != key) { + if (key < node->key) { + if (!node->left) + node->left = new Node(key, node); + node = node->left; + } else { + if (!node->right) + node->right = new Node(key, node); + node = node->right; + } + } + } + + // Return successor of the given node. + // + // The successor of a node is the node with the next higher key. + // Return nullptr if there is no such node. + // If the argument is nullptr, return the node with the smallest key. + Node* successor(Node* node) { + // TODO: Implement + } + + // Destructor to free all allocated memory. + ~Tree() { + Node* node = root; + while (node) { + Node* next; + if (node->left) { + next = node->left; + node->left = nullptr; + } else if (node->right) { + next = node->right; + node->right = nullptr; + } else { + next = node->parent; + delete node; + } + node = next; + } + } +}; diff --git a/01-tree_successor/cpp/tree_successor_test.cpp b/01-tree_successor/cpp/tree_successor_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8b794af982614769d379d77b4f896f5803bdffec --- /dev/null +++ b/01-tree_successor/cpp/tree_successor_test.cpp @@ -0,0 +1,54 @@ +#include <algorithm> +#include <functional> +#include <cstdint> +#include <string> +#include <utility> +#include <vector> + +#include "tree_successor.h" + +using namespace std; + +// If the condition is not true, report an error and halt. +#define EXPECT(condition, message) do { if (!(condition)) expect_failed(message); } while (0) +void expect_failed(const string& message); + +void test(const vector<int>& sequence) { + Tree tree; + for (const auto& element : sequence) + tree.insert(element); + + vector<int> sorted_sequence(sequence); + sort(sorted_sequence.begin(), sorted_sequence.end()); + + Node* node = tree.successor(nullptr); + for (const auto& element : sorted_sequence) { + EXPECT(node, "Expected successor " + to_string(element) + ", got nullptr"); + EXPECT(node->key == element, + "Expected successor " + to_string(element) + ", got " + to_string(node->key)); + node = tree.successor(node); + } + EXPECT(!node, "Expected no successor, got " + to_string(node->key)); +} + +vector<pair<string, function<void()>>> tests = { + {"right_path", [] + { vector<int> numbers; + for (int i = 0; i < 10000; i++) numbers.push_back((int)(7.13*i)); + test(numbers); + } + }, + {"left_path", [] + { vector<int> numbers; + for (int i = 9999; i >= 0; i--) numbers.push_back((int)(7.13*i)); + test(numbers); + } + }, + {"random_tree", [] + { vector<int> numbers = {997}; + for (int i = 2; i < 199999; i++) + numbers.push_back((numbers.back() * int64_t(997)) % 199999); + test(numbers); + } + }, +}; diff --git a/01-tree_successor/python/tree_successor.py b/01-tree_successor/python/tree_successor.py new file mode 100644 index 0000000000000000000000000000000000000000..d0377ab6cf0725f68a0f4542a97b78aadc1fcfa7 --- /dev/null +++ b/01-tree_successor/python/tree_successor.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 + +class Node: + """Node in a binary tree `Tree`""" + + def __init__(self, key, left=None, right=None, parent=None): + self.key = key + self.left = left + self.right = right + self.parent = parent + +class Tree: + """A simple binary search tree""" + + def __init__(self, root=None): + self.root = root + + def insert(self, key): + """Insert key into the tree. + + If the key is already present, do nothing. + """ + if self.root is None: + self.root = Node(key) + return + + node = self.root + while node.key != key: + if key < node.key: + if node.left is None: + node.left = Node(key, parent=node) + node = node.left + else: + if node.right is None: + node.right = Node(key, parent=node) + node = node.right + + def successor(self, node=None): + """Return successor of the given node. + + The successor of a node is the node with the next greater key. + Return None if there is no such node. + If the argument is None, return the node with the smallest key. + """ + # TODO: Implement + raise NotImplementedError diff --git a/01-tree_successor/python/tree_successor_test.py b/01-tree_successor/python/tree_successor_test.py new file mode 100644 index 0000000000000000000000000000000000000000..41d691423275ead31f514068f9e27913f195c871 --- /dev/null +++ b/01-tree_successor/python/tree_successor_test.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +import sys + +import tree_successor + +def test_sequence(sequence): + tree = tree_successor.Tree() + for i in sequence: tree.insert(i) + + node = tree.successor(None) + for element in sorted(sequence): + assert node is not None, "Expected successor {}, got None".format(element) + assert node.key == element, "Expected successor {}, got {}".format(element, node.key) + node = tree.successor(node) + assert node is None, "Expected no successor, got {}".format(node.key) + +def gen_increasing_seq(): + return [int(7.13*i) for i in range(10000)] + +tests = [ + ("right_path", lambda: test_sequence(gen_increasing_seq())), + ("left_path", lambda: test_sequence(list(reversed(gen_increasing_seq())))), + ("random_tree", lambda: test_sequence([pow(997, i, 199999) for i in range(1, 199999)])), +] + +if __name__ == "__main__": + for required_test in sys.argv[1:] or [name for name, _ in tests]: + for name, test in tests: + if name == required_test: + print("Running test {}".format(name), file=sys.stderr) + test() + break + else: + raise ValueError("Unknown test {}".format(name)) diff --git a/01-tree_successor/task.md b/01-tree_successor/task.md new file mode 100644 index 0000000000000000000000000000000000000000..eb2187e4730cf2624feb5b43db16c564dcb97fed --- /dev/null +++ b/01-tree_successor/task.md @@ -0,0 +1,13 @@ +Given an implementation of a simple binary search tree including parent +pointers, implement a `successor` method. The methods is given a node +and it should return another node of the tree with the next higher key +(i.e., the smallest of keys which are greater than the given one). + +- If there is no such node, it should return a null pointer. +- The given node can also be a null pointer, in which case the method should + return the smallest node in the tree. + +You can expect that `successor` method is never called on an empty tree. + +You should submit the file `tree_successor.*` (but not the +`tree_successor_test.*`).