Skip to content
Snippets Groups Projects
Commit 862acc35 authored by Petr Chmel's avatar Petr Chmel
Browse files

Publish tree successor

parent 7ff73d98
No related branches found
No related tags found
No related merge requests found
test: tree_successor_test
./$<
CXXFLAGS=-std=c++23 -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
#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;
}
// 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.
// If the node is given, start searching a new position from that node.
Node* insert(int key, Node *node = nullptr) {
if (!root) {
root = new Node(key);
return root;
}
if (!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 node;
}
// 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;
}
}
};
#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) {
Node* node = tree.successor(nullptr);
for (const auto& element : 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<int> get_linear_sequence() {
vector<int> numbers;
for (int i = 0; i < 10000000; i++)
numbers.push_back((int)(7.13*i));
return numbers;
}
void test_path(bool right) {
vector<int> numbers = get_linear_sequence();
Tree tree;
Node *node = nullptr;
if (right)
for (int key : numbers)
node = tree.insert(key, node);
else
for (int index = numbers.size() - 1; index >= 0; --index)
node = tree.insert(numbers[index], node);
test(numbers, tree);
}
void test_two_paths() {
vector<int> numbers = get_linear_sequence();
Tree tree;
Node *node = nullptr;
for(size_t i = numbers.size()/2; i < numbers.size(); i++)
node = tree.insert(numbers[i], node);
node = nullptr;
for(int i = numbers.size()/2 - 1; i >= 0; i--)
node = tree.insert(numbers[i], node);
test(numbers, tree);
}
void test_sequence(vector<int> &&sequence) {
Tree tree;
for (const auto& element : sequence)
tree.insert(element);
sort(sequence.begin(), sequence.end());
test(sequence, tree);
}
void test_random() {
vector<int> sequence = {997};
for (int i = 2; i < 199999; i++)
sequence.push_back((sequence.back() * int64_t(997)) % 199999);
test_sequence(move(sequence));
}
void test_trivial() {
test_sequence({5});
test_sequence({7,9});
test_sequence({7,3});
test_sequence({5,3,7});
}
void test_comb() {
vector<int> numbers = get_linear_sequence();
Tree tree;
Node *node = nullptr;
for(size_t i = numbers.size()/2; i < numbers.size(); i++)
node = tree.insert(numbers[i], node);
node = nullptr;
for(int i = numbers.size()/2 - 1; i >= 0; i-=2) {
node = tree.insert(numbers[i-1], node);
tree.insert(numbers[i], node);
}
test(numbers, tree);
}
vector<pair<string, function<void()>>> tests = {
{ "trivial", test_trivial },
{ "right_path", []{ test_path(true); } },
{ "left_path", []{ test_path(false); } },
{ "random_tree", test_random },
{ "two_paths", test_two_paths },
{ "comb", test_comb }
};
#!/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, node=None):
"""Insert key into the tree.
If the key is already present, do nothing.
If the node is given, start searching a new position from that node.
"""
if self.root is None:
self.root = Node(key)
return self.root
if not node:
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
return node
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
#!/usr/bin/env python3
import sys
from tree_successor import Tree
def test_tree(tree, sequence):
node = tree.successor(None)
for element in 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 test_sequence(sequence):
tree = Tree()
for i in sequence:
tree.insert(i)
sequence.sort()
test_tree(tree, sequence)
def test_trivial_tree():
test_sequence([5])
test_sequence([7,9])
test_sequence([7,3])
test_sequence([5,3,7])
def test_random_tree():
test_sequence([pow(997, i, 199999) for i in range(1, 199999)])
def test_path(right):
sequence = [int(7.13*i) for i in range(1000000)]
tree = Tree()
node = None
sequence_insert = sequence if right else reversed(sequence)
for key in sequence_insert:
node = tree.insert(key, node)
test_tree(tree, sequence)
def test_two_paths():
sequence_left = [int(7.13*i) for i in range(1000000)]
sequence_right = [int(7.13*i) for i in range(1000000, 2000000)]
tree = Tree()
node = None
for key in sequence_right:
node = tree.insert(key, node)
node = None
for key in reversed(sequence_left):
node = tree.insert(key, node)
test_tree(tree, sequence_left + sequence_right)
def test_comb():
sequence = [int(7.13*i) for i in range(1000000)]
tree = Tree()
node = None
for i in range(len(sequence)//2, len(sequence)):
node = tree.insert(sequence[i], node)
node = None
for i in range(len(sequence)//2-1, 0, -2):
node = tree.insert(sequence[i-1], node)
tree.insert(sequence[i], node)
test_tree(tree, sequence)
tests = [
("trivial", test_trivial_tree),
("random_tree", test_random_tree),
("right_path", lambda: test_path(True)),
("left_path", lambda: test_path(False)),
("two_paths", test_two_paths),
("comb", test_comb),
]
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))
# `tree_successor`
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.*`).
Source code templates can be found in [the git repository](https://gitlab.kam.mff.cuni.cz/datovky/assignments/-/tree/master).
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment